Hi,
I am generating a Custom Control (“Offer Panel”) in Umbraco Newsletter Studio that contains a few different fields, including an image field for said control. Said control is for use in transactional emails and the like.
My Umbraco version is 13.2.4.0, with Newsletter Studio version 13.0.18 installed. I have not yet secured a license, as I wanted to test the capabilities of the software before we purchased it.
In my current test transactional email, I’ve encountered two issues I can’t seem to work out:
-
Even if the current transactional email template already has and image saved on my custom control, on initial load of the content section, the images in my custom control will not initially be loaded. They only load after you click and “hydrate,” the data on said controller.

-
Additionally, if I add the pre-installed “image” field to my email (which is likely as I am looking to use it for email Header Images), it creates a bug where clicking into the image control and then the Custom control, it seems to, “Remove,” the image from my custom control, both in the left side Email builder section and the Edit panel to the right?

If the images are saved and we go straight to the Email Preview, it does seem to still work correctly and from test sending, emails will contain the images. I believe there must be an issue in how I’ve implemented things causing this issue, since the already provided controls work as intended!
Happy to provide any of my code as needed, but I will include where I would imagine the issue is, in my Edit/View.html for my Custom “Offer Panel” Control and the Edit Controller.
Edit.html
<div ng-controller="NewsletterStudioCustom.OfferPanel.EditController as editVm"
ng-init="editVm.init(vm,vm.selectedControl)">
<div class="ns-property-group">
<ns-umb-propertylabel="ns_control_offerPanel_image" description="">
<button type="button" class="btn btn-default" ng-click="editVm.pickImage()">
Pick Image
</button>
<div ng-if="editVm.imageUrl" style="margin-top:16px; color:#666;">
Selected:
<img ng-src="{{ editVm.imageUrl }}" style="width: 150px; display: block; height: 145px; margin: 10px 0; object-fit: cover;">
</div>
<div ng-if="!editVm.imageUrl" style="margin-top:6px; color:#999;">
No image selected
</div>
</ns-umb-property>
</div>
<div class="ns-property-group">
<ns-umb-property label="ns_control_offerPanel_title" description="">
<input type="text" class="umb-textstring" ng-model="editVm.control.title" maxlength="120" />
</ns-umb-property>
</div>
<div class="ns-property-group">
<ns-umb-property label="ns_control_offerPanel_body" description="">
<textarea ng-model="editVm.control.bodyHtml" rows="10" style="width:100%"></textarea>
</ns-umb-property>
</div>
<div class="ns-property-group">
<ns-umb-property label="ns_control_offerPanel_buttonLabel" description="">
<input type="text" class="umb-textstring" ng-model="editVm.control.buttonLabel" />
</ns-umb-property>
</div>
<div class="ns-property-group">
<ns-umb-property label="ns_control_offerPanel_buttonUrl" description="">
<input type="text" class="umb-textstring" ng-model="editVm.control.buttonUrl" placeholder="https://..." />
</ns-umb-property>
</div>
<div class="ns-property-group">
<ns-umb-property label="ns_control_offerPanel_bg" description="">
<input type="text" class="umb-textstring" ng-model="editVm.control.backgroundColor" placeholder="#ffffff" />
</ns-umb-property>
</div>
<div class="ns-property-group">
<ns-umb-property label="ns_control_offerPanel_btnBg" description="">
<input type="text" class="umb-textstring" ng-model="editVm.control.buttonBackground" placeholder="#0078d4" />
</ns-umb-property>
</div>
<div class="ns-property-group">
<ns-umb-property label="ns_control_offerPanel_btnText" description="">
<input type="text" class="umb-textstring" ng-model="editVm.control.buttonTextColor" placeholder="#ffffff" />
</ns-umb-property>
</div>
<div class="ns-property-group">
<ns-umb-property label="ns_control_offerPanel_padding" description="">
<ns-margin-editor ng-model="editVm.control.padding"></ns-margin-editor>
</ns-umb-property>
</div>
</div>
View.html
<div ns-control>
<div ng-style="{
'padding-top': control.padding.top,
'padding-bottom': control.padding.bottom,
'padding-left': control.padding.left,
'padding-right': control.padding.right,
'background': control.backgroundColor
}" style="display: flex;">
<div class="ns-image" style="text-align:center; margin-bottom:16px; width: 270px;">
<div ng-if="control.value.imageUrl"
style="width:100%; height:140px; background:#f0f0f0; color:#888; display:flex; align-items:center; justify-content:center; overflow: hidden;">
<img ng-src="{{control.value.imageUrl}}"
style="width: 100%; object-fit: cover; object-position: center;">
</div>
<div ng-if="!control.value.imageUrl"
style="width:100%; height:140px; background:#fff3cd; color:#328186; display:flex; align-items:center; justify-content:center;">
No image selected
</div>
</div>
<div class="content" style="display: flex; flex-direction: column; justify-content: center; margin-left: 20px;">
<div style="font-size:18px; margin:0 8px 8px 8px; color: #153538;text-decoration: none;text-transform: uppercase;font-family: Calibri, Candara, Open Sans, Arial, Tahoma, Helvetica, sans-serif;">
{{control.title}}
</div>
<div style="margin:0 8px 16px 8px;">
<ns-html html="control.bodyHtml"></ns-html>
</div>
<div style="margin:0 8px 8px 8px;">
<span style="display:block; padding:8px 12px; text-align: center;"
ng-style="{
'background': control.buttonBackground || '#328186',
'color': control.buttonTextColor || '#ffffff'
}">
{{control.buttonLabel || 'Learn more'}}
</span>
</div>
</div>
</div>
</div>
Edit.Controller.js
angular.module("umbraco").controller("NewsletterStudioCustom.OfferPanel.EditController",
["$scope", "editorService", "entityResource", "mediaResource", function ($scope, editorService, entityResource, mediaResource) {
var editVm = this;
editVm.init = function (emailEditor, control) {
editVm.control = control;
if ($scope.model && $scope.model.value) { editVm.imageUrl = $scope.model.value.imageUrl; }
resolveImage();
};
editVm.pickImage = function () {
editorService.mediaPicker({
multiPicker: false,
submit: function (model) {
var item = (model && model.selection && model.selection[0]) || null;
if (item) {
editVm.control.imageUdi = item.udi;
resolveImage();
}
editorService.close();
},
close: function () {
editorService.close();
}
});
};
function resolveImage() {
if (!editVm.control || !editVm.control.imageUdi) {
editVm.control.value = editVm.control.value || {};
editVm.control.value.imageUrl = null;
editVm.imageUrl = null;
if (!$scope.$$phase) $scope.$applyAsync();
return;
}
entityResource.getById(editVm.control.imageUdi, "Media").then(function (ent) {
mediaResource.getById(ent.id).then(function (media) {
editVm.imageUrl = media.mediaLink;
editVm.control.value = editVm.control.value || {};
editVm.control.value.imageUrl = media.mediaLink;
if (!$scope.$$phase) $scope.$applyAsync();
});
});
}
var evtHandler = $scope.$on("nsSelectedControlChanged", function ($event, ctrl) {
editVm.init(null, ctrl);
});
var controlInitHandler = $scope.$on("nsControlInitialized", function ($event, ctrl) {
if (!ctrl.value) ctrl.value = {};
if (ctrl.imageUdi) {
entityResource.getById(ctrl.imageUdi, "Media").then(function (ent) {
mediaResource.getById(ent.id).then(function (media) {
ctrl.value.imageUrl = media.mediaLink;
});
});
} else {
ctrl.value.imageUrl = null;
}
});
$scope.$on("$destroy", function () {
evtHandler();
controlInitHandler();
});
}]
);
Any help here would be greatly appreciated!!

