Implementation Examples
Below are practical examlpes demonstrating how to integrate the component into your templates:
<!-- Basic usage for media ID selection -->
<script id="mediaIdTemplate" type="text/html">
<media-choice-button params="bindingValue: media_id"></media-choice-button>
</script>
<!-- Advanced usage: News article selection with locked type -->
<script id="articleTemplate" type="text/html">
<media-choice-button params="bindingValue: media_id, defaultType: 5, isSelectionDisabled: true"></media-choice-button>
</script>
Configuration Parameters
- bindingValue: An observable holding the WeChat media identifier.
- defaultType: Specifies the initial media category. Accepted values are: 0 (Image), 1 (Audio), 2 (Video), 5 (News Article).
- isSelectionDisabled: A boolean flag to prevent users from changing the media type dropdown.
Component Source Code
The following implementation defines the ViewModel and registers the custom component with KnockoutJS:
(function() {
'use strict';
// ViewModel factory function
var createComponentInstance = function (params, componentInfo) {
var vm = this;
// Observable properties for UI binding
vm.displayTitle = ko.observable('Select Material');
vm.previewUrl = ko.observable('/Content/placeholders/default-thumbnail.png');
vm.selectedMediaId = ko.observable('');
// Configuration state
vm.currentMediaType = ko.observable('1');
vm.lockMediaType = ko.observable(false);
// Reset display when media type changes
vm.currentMediaType.subscribe(function (newType) {
console.log('Media type switched to:', newType);
vm.displayTitle('Select Material');
vm.previewUrl('/Content/placeholders/default-thumbnail.png');
vm.selectedMediaId('');
});
// Open the resource picker modal
vm.openResourcePicker = function () {
var mediaType = vm.currentMediaType();
var pickerUrl = '';
var urlMappings = {
'5': '/Site_News?resourceType=5&lightLayout=1', // News
'0': '/Site_Resources?resourceType=0&lightLayout=1', // Image
'1': '/Site_Resources?resourceType=1&lightLayout=1', // Audio
'2': '/Site_Resources?resourceType=2&lightLayout=1' // Video
};
pickerUrl = urlMappings[mediaType] || '';
if (pickerUrl) {
mwc.window.show('Choose Material', pickerUrl);
}
};
// Initialize from passed parameters
if (params && typeof params.bindingValue() === 'string') {
// Sync external value with internal state
params.bindingValue.subscribe(function (newValue) {
vm.selectedMediaId(newValue);
});
// Load existing media data if available
if (params.bindingValue().length > 0) {
vm.selectedMediaId(params.bindingValue());
mwc.restApi.get({
url: '/Site_Resources/GetJsonDataByMediaId/' + vm.selectedMediaId(),
isBlockUI: true,
blockUI: componentInfo.element,
success: function (response) {
var resourceData = response.FileBase;
if (!resourceData) return;
// Update type first to avoid subscription clearing data
vm.currentMediaType(response.ResourceType);
vm.displayTitle(resourceData.Name);
vm.previewUrl(resourceData.FrontCoverImageUrl || resourceData.Url || resourceData.SiteUrl);
vm.selectedMediaId(resourceData.MediaId);
}
});
}
// Apply configuration overrides
if (params.defaultType !== undefined) {
vm.currentMediaType(String(params.defaultType));
}
if (params.isSelectionDisabled !== undefined) {
vm.lockMediaType(params.isSelectionDisabled);
}
}
// Global callback for resource selection
window.handleResourceSelection = function (resource) {
params.bindingValue(resource.MediaId);
vm.selectedMediaId(resource.MediaId);
vm.displayTitle(resource.Name);
vm.previewUrl(resource.FrontCoverImageUrl || resource.Url || resource.SiteUrl);
};
};
// Component registration
ko.components.register('media-choice-button', {
viewModel: {
createViewModel: function (params, componentInfo) {
return new createComponentInstance(params, componentInfo);
}
},
template:
'<div class="form-group">' +
'<label class="col-sm-2 control-label">Type:</label>' +
'<div class="col-sm-10">' +
'<select class="form-control" data-bind="value: currentMediaType, disable: lockMediaType">' +
'<option value="0">Image</option>' +
'<option value="1">Audio</option>' +
'<option value="2">Video</option>' +
'<option value="5">News Article</option>' +
'</select>' +
'</div>' +
'</div>' +
'<div class="form-group">' +
'<div class="col-sm-12">' +
'<div class="panel panel-default" data-bind="click: openResourcePicker">' +
'<div class="panel-body">' +
'<!-- Image preview -->' +
'<p data-bind="if: currentMediaType() === \'0\'">' +
'<img style="width:150px;height:100px" data-bind="attr: {src: previewUrl}" />' +
'</p>' +
'<!-- Audio player -->' +
'<p data-bind="if: currentMediaType() === \'1\' && selectedMediaId() !== \'\'">' +
'<audio style="width:250px;height:100px" controls data-bind="attr: {src: previewUrl}"></audio>' +
'</p>' +
'<!-- Video player -->' +
'<p data-bind="if: currentMediaType() === \'2\' && selectedMediaId() !== \'\'">' +
'<video style="height:100px;width:250px" controls data-bind="attr: {src: previewUrl}"></video>' +
'</p>' +
'<!-- News article thumbnail -->' +
'<p data-bind="if: currentMediaType() === \'5\'">' +
'<img style="width:150px;height:100px" data-bind="attr: {src: previewUrl}" />' +
'</p>' +
'<div>' +
'<p data-bind="text: displayTitle"></p>' +
'</div>' +
'<!-- Placeholder button for empty audio/video -->' +
'<!-- ko if: selectedMediaId() === \'\' && ["1", "2"].includes(currentMediaType()) -->' +
'<p>' +
'<a class="btn btn-primary btn-block m-t" data-bind="click: $root.openResourcePicker">' +
'<i class="fa fa-plus"></i> Select Material' +
'</a>' +
'</p>' +
'<!-- /ko -->' +
'</div>' +
'</div>' +
'</div>' +
'</div>'
});
})();
Component Output
The rendered component provides a dropdown selector for media types and a clickable panel area that displays the selected resource preview with appropriate rendering based on the content type.