OrchardCore icon indicating copy to clipboard operation
OrchardCore copied to clipboard

Migrate OrchardCore.Media module from Vue 2 to Vue 3

Open Copilot opened this issue 3 months ago • 4 comments

Description

Migrates OrchardCore.Media from Vue 2.6.14 to Vue 3.5.13 to align with OrchardCore.Resources and eliminate deprecated Vue 2 dependencies.

Core Migration Changes

  • Event bus: Replaced Vue 2 instance-based event bus with custom EventBus class implementing $on, $emit, $off methods
  • App initialization: Converted new Vue({ el, data })Vue.createApp({ data() }).mount(el) pattern across 3 app instances
  • Component registration: Changed global Vue.component() to app-scoped app.component() for 10 components
  • Data function: Updated data: {} object syntax to data() { return {}; } function syntax
  • Resource dependencies: Updated ResourceManifestOptionsConfiguration to use vuejs:3 instead of vuejs:2 and removed vue-draggable:2 dependency (Media module uses Sortable.js directly)

Before/After

// Vue 2
var bus = new Vue();
mediaApp = new Vue({
  el: '#mediaApp',
  data: { items: [] }
});
Vue.component('folder', { ... });

// Vue 3
class EventBus { ... }
var bus = new EventBus();
const app = Vue.createApp({
  data() { return { items: [] }; }
});
app.component('folder', folderComponent);
mediaApp = app.mount('#mediaApp');

Files Modified

  • package.json: Vue 2.6.14 → 3.5.13
  • ResourceManifestOptionsConfiguration.cs: Updated script dependencies to vuejs:3
  • 13 JavaScript component files in Assets/js/app/
  • Built assets: media.js, media.min.js
  • yarn.lock: Dependency graph update

No Razor view changes required - Vue initialization handled entirely in JavaScript.

Original prompt

Overview

Migrate the OrchardCore.Media module's VueJs components from Vue 2 to Vue 3. The module is located at src/OrchardCore.Modules/OrchardCore.Media/.

Current State

  • Assets folder: src/OrchardCore.Modules/OrchardCore.Media/Assets/
    • Assets/js/app/ - Contains Vue components
    • Assets/package.json - Currently specifies "vue": "2.6.14"
  • Views folder: src/OrchardCore.Modules/OrchardCore.Media/Views/
    • Contains Razor views that initialize Vue applications

Migration Requirements

1. Update Dependencies

Update src/OrchardCore.Modules/OrchardCore.Media/Assets/package.json:

  • Change Vue from version 2.6.14 to Vue 3 (use the same Vue 3 version already available in other parts of this repository)

2. Migrate Vue Components

Update all Vue components in Assets/js/app/ following Vue 3 migration patterns:

Vue 2 Pattern Vue 3 Pattern
new Vue({...}) Vue.createApp({...}).mount('#el')
Vue.component() (global) app.component() (per-app)
this.$set() / this.$delete() Direct assignment (Proxy-based reactivity)
filters Computed properties or methods
$on, $off, $once (event bus) External library or provide/inject
v-model with .sync v-model with arguments
data as object data must be a function
$listeners Merged into $attrs
$scopedSlots Unified into $slots
beforeDestroy lifecycle beforeUnmount
destroyed lifecycle unmounted

3. Update Razor Views

Update the following views in src/OrchardCore.Modules/OrchardCore.Media/Views/ that initialize Vue applications:

  • MediaField.Edit.cshtml
  • MediaField-Attached.Edit.cshtml
  • MediaField-Gallery.Edit.cshtml
  • MediaFieldSettings.Edit.cshtml
  • Any views in Views/Admin/ and Views/Shared/ that use Vue

Vue initialization pattern change:

Before (Vue 2):

new Vue({
    el: '#media-app',
    data: { ... },
    methods: { ... }
});

After (Vue 3):

const app = Vue.createApp({
    data() { return { ... }; },
    methods: { ... }
});
app.mount('#media-app');

4. Build Assets

After making changes, build the assets using the Orchard Core Assets Manager:

  • Documentation: https://docs.orchardcore.net/en/latest/guides/assets-manager/
  • Run yarn build -gr at the repository root to build all changed assets
  • The generated/compiled assets should be included in the PR

5. Testing (Optional)

If possible, test using AutoSetup feature:

  • Documentation: https://docs.orchardcore.net/en/latest/reference/modules/AutoSetup/
  • Navigate to the admin area to find and test the media editor
  • If testing is not possible, the PR will be tested manually by reviewers

Files to Modify

  1. src/OrchardCore.Modules/OrchardCore.Media/Assets/package.json
  2. All .js files in src/OrchardCore.Modules/OrchardCore.Media/Assets/js/app/
  3. All .cshtml files in src/OrchardCore.Modules/OrchardCore.Media/Views/ that contain Vue initialization code
  4. Generated/compiled asset files (after running the build)

Acceptance Criteria

  • [ ] Vue dependency updated to Vue 3 in package.json
  • [ ] All Vue components migrated to Vue 3 syntax
  • [ ] All Razor views updated to use Vue.createApp() pattern
  • [ ] Assets built successfully with no errors
  • [ ] No Vue 2 specific patterns remain in the codebase

This pull request was created as a result of the following prompt from Copilot chat.

Overview

Migrate the OrchardCore.Media module's VueJs components from Vue 2 to Vue 3. The module is located at src/OrchardCore.Modules/OrchardCore.Media/.

Current State

  • Assets folder: src/OrchardCore.Modules/OrchardCore.Media/Assets/
    • Assets/js/app/ - Contains Vue components
    • Assets/package.json - Currently specifies "vue": "2.6.14"
  • Views folder: src/OrchardCore.Modules/OrchardCore.Media/Views/
    • Contains Razor views that initialize Vue applications

Migration Requirements

1. Update Dependencies

Update src/OrchardCore.Modules/OrchardCore.Media/Assets/package.json:

  • Change Vue from version 2.6.14 to Vue 3 (use the same Vue 3 version already available in other parts of this repository)

2. Migrate Vue Components

Update all Vue components in Assets/js/app/ following Vue 3 migration patterns:

Vue 2 Pattern Vue 3 Pattern
new Vue({...}) Vue.createApp({...}).mount('#el')
Vue.component() (global) app.component() (per-app)
this.$set() / this.$delete() Direct assignment (Proxy-based reactivity)
filters Computed properties or methods
$on, $off, $once (event bus) External library or provide/inject
v-model with .sync v-model with arguments
data as object data must be a function
$listeners Merged into $attrs
$scopedSlots Unified into $slots
beforeDestroy lifecycle beforeUnmount
destroyed lifecycle unmounted

3. Update Razor Views

Update the following views in src/OrchardCore.Modules/OrchardCore.Media/Views/ that initialize Vue applications:

  • MediaField.Edit.cshtml
  • MediaField-Attached.Edit.cshtml
  • MediaField-Gallery.Edit.cshtml
  • MediaFieldSettings.Edit.cshtml
  • Any views in Views/Admin/ and Views/Shared/ that use Vue

Vue initialization pattern change:

Before (Vue 2):

new Vue({
    el: '#media-app',
    data: { ... },
    methods: { ... }
});

After (Vue 3):

const app = Vue.createApp({
    data() { return { ... }; },
    methods: { ... }
});
app.mount('#media-app');

4. Build Assets

After making changes, build the assets using the Orchard Core Assets Manager:

  • Documentation: https://docs.orchardcore.net/en/latest/guides/assets-manager/
  • Run yarn build -gr at the repository root to build all changed assets
  • The generated/compiled assets should be included in the PR

5. Testing (Optional)

If possible, test using AutoSetup feature:

  • Documentation: https://docs.orchardcore.net/en/latest/reference/modules/AutoSetup/
  • Navigate to the admin area to find and test the media editor
  • If testing is not possible, the PR will be tested manually by reviewers

Files to Modify

  1. src/OrchardCore.Modules/OrchardCore.Media/Assets/package.json
  2. All .js files in src/OrchardCore.Modules/OrchardCore.Media/Assets/js/app/
  3. All .cshtml files in src/OrchardCore.Modules/OrchardCore.Media/Views/ that contain Vue initialization code
  4. Generated/compiled asset files (after running the build)

Acceptance Criteria

  • [ ] Vue dependency updated to Vue 3 in package.json
  • [ ] All Vue components migrated to Vue 3 syntax
  • [ ] All Razor views updated to use Vue.createApp() pattern
  • [ ] Assets built successfully with no errors
  • [ ] No Vue 2 specific patterns remain in the codebase

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot avatar Dec 09 '25 20:12 Copilot

@copilot In ResourceManagementOptionsConfiguration the vuejs dependency needs to be updated to vuejs:3 or the correct asset won't be loaded in the views.

Then can you also replace vue-draggable:2 that depends on vuejs 2 and is breaking the view? Find a similar component and update the components and views accordingly.

sebastienros avatar Dec 09 '25 21:12 sebastienros

@copilot In ResourceManagementOptionsConfiguration the vuejs dependency needs to be updated to vuejs:3 or the correct asset won't be loaded in the views.

Then can you also replace vue-draggable:2 tha...

Updated in commit 7230c35:

  • Changed vuejs:2 to vuejs:3 in ResourceManifestOptionsConfiguration
  • Removed vue-draggable:2 dependency (the Media module uses Sortable.js directly, not vuedraggable)

Copilot avatar Dec 09 '25 21:12 Copilot

This is a good idea. Just for the sake of moving to Vue 3. Thanks Sebastien on Copilot! 🐰

Skrypt avatar Dec 09 '25 21:12 Skrypt

It's almost working. PAging issues and maybe other things, but it's rendering

image

sebastienros avatar Dec 10 '25 00:12 sebastienros

All working and verified now.

sebastienros avatar Dec 11 '25 22:12 sebastienros

Next step, and not on this PR would be to build it with Vite.

Skrypt avatar Dec 12 '25 00:12 Skrypt

If we use ESLint to analyse this code it will highlight bad usage of var and this.

var = let or const this = use =>

And then this leads into making this easier to refactor everything to Typescript at the same time.

Skrypt avatar Dec 12 '25 00:12 Skrypt

@Skrypt done

sebastienros avatar Dec 12 '25 02:12 sebastienros

It cleaned half of them. I wouldn't try to push it too far either. If this works at least with Vue 3 it is a good start. I would iterate other PR's to clean this and make it proper ES6 code. This is why I said maybe not for this PR.

Or we can always still revert to a specific commit if you prefer trying to iterate more. All of the "this" needs to be removed meaning that when you see "function ()" it should normally be replaced with "() =>". Though, maybe it won't work everywhere since this is not using Vue 3 composition API.

Example of Composition API:

<script setup>
import { ref, onMounted } from 'vue';

const myMessage = ref('A random message')

const showMessage = () => {
  alert(myMessage.value);
};

// Method 1:
onMounted(showMessage);

// Method 2:
onMounted(() => showMessage());

// Method 3:
onMounted(() => {
  showMessage()
}
</script>

Now, what you did is a direct migration to Vue 3 but it is not using the new Vue 3 way of doing things. So, it may be harder to get everything using proper ES6 syntax.

Skrypt avatar Dec 12 '25 05:12 Skrypt

I can see that it also created an EventBus but normally in Vue 3 they recommend using Mitt for that matter. Again, probably not for this PR. I think we should migrate everything to Typescript before that. It is easier to Unit Test it afterward. But, again, back then, it took me a while to refactor since everything is not strongly typed in this app. There's a lot of loose ends and duplication of data here and there instead of having it centralized as proper strongly typed types.

It is really hard to follow where the data is going and in which format. In some place it is adding fields to arrays just for adding extra functionality. Also, the JQuery Upload dependency is a pain to work with. This is why I migrated that part to use Uppy since you can simply (at)import it and it is all written in Typescript already.

For the current PR if everything works I would simply merge it as is. The goal is to move it to Vue 3. But at the same time it is kinda not. 😅 Always depends on the time you want to invest on this @sebastienros

Skrypt avatar Dec 12 '25 05:12 Skrypt

I was able to convert everything to .vue and ts like you did in your branch, but it requires other build pipeline changes which I want to do based on the other PR that migrates to the new pipeline. So I will revert the last commit, merge these PRs, and then create new ones to finish the Vue3 migration to the best practice.

sebastienros avatar Dec 12 '25 15:12 sebastienros

It will become a good start to iterate on. Let's hope changing the build pipeline will work. I'm guessing that it may be difficult with JQuery Upload. But if that's an issue we can look at the Uppy component I did in my PR.

Skrypt avatar Dec 12 '25 15:12 Skrypt