CRM 2013 custom notification in the entity form - notifications

I need to show custom warning message in entity from if specific field is not empty (on change).
I wrote in a different company global JS file the code that shows the message:
addNotification: function (message) { //Adds a warning message on the top of the entity Form
var notificationHTML = '<DIV class="Notification"><TABLE cellSpacing="0" cellPadding="0"><TBODY><TR><TD vAlign="top"><IMG class="ms-crm-Lookup-Item" alt="" src="/_imgs/error/notif_icn_crit16.png" /></TD><TD><SPAN>' + message + '</SPAN></TD></TR></TBODY></TABLE></DIV>';
var notificationsArea = document.getElementById('Notifications');
if (notificationsArea == null) return;
notificationsArea.innerHTML += notificationHTML;
notificationsArea.style.display = 'block';
},
and in my entity JS file I wrote this:
function checkIfAssignToIsEmpty() {
var AssignTo = Xrm.Page.getAttribute('el_assign_to').getValue();
if (AssignTo != null)
newNotification();
}
function newNotification() {
var assignToVal = Xrm.Page.getAttribute("el_assign_to").getValue();
var newNotification = GlobalFunction.addNotification('assiignToVal + The Task will assign to');
}
I get error message after I choose value in my field
Object doesn't support property or method 'addNotification'
Can you please help?

You can use the supported methods for notification
Xrm.Page.ui.setFormNotification(message, level, uniqueId);
and
Xrm.Page.ui.clearFormNotification(uniqueId);
There are also notification for the controls. You can find several examples here:
http://garethtuckercrm.com/2013/10/17/crm-2013-new-features-javascript-notifications/

Related

How to prevent closing of cell edit mode on validation errors with custom vue components in ag-grid

I have succesfully rendered my own component as the cellEditor and would like and on-leave I would like it to try to validate the value and prevent the closing if it fails.
If I look at this then https://www.ag-grid.com/javascript-grid-cell-editing/#editing-api there's cancelable callback functions for editing. But in this callback function is there a way to access the current instantiated component? I would think that would be the easiest way to handle this.
I'm using vee-validate so the validation function is async, just to keep in mind.
Use Full row editing.
Create a global variable like
var problemRow = -1;
Then Subscribe to this events:
onRowEditingStarted: function (event) {
if (problemRow!=-1 && event.rowIndex!=problemRow) {
gridOptions.api.stopEditing();
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
},
onRowEditingStopped: function (event) {
if (problemRow==-1) {
if (event.data.firstName != "your validation") {
problemRow = event.rowIndex
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
}
if (problemRow == event.rowIndex) {
if (event.data.firstName != "your validation") {
problemRow = event.rowIndex
gridOptions.api.startEditingCell({
rowIndex: problemRow,
colKey: 'the column you want to focus',
});
}
else{
problemRow=-1;
}
}
},
I had a similar issue - albeit in AngularJS and the non-Angular mode for ag-grid - I needed to prevent the navigation when the cell editor didn't pass validation.
The documentation is not very detailed, so in the end I added a custom cell editor with a form wrapped around the input field (to handle the niceties such as red highlighting etc), and then used Angular JS validation. That got me so far, but the crucial part was trying to prevent the user tabbing out or away when the value was invalid so the user could at least fix the issue.
I did this by adding a value parser when adding the cell, and then within that if the value was invalid according to various rules, throw an exception. Not ideal, I know - but it does prevent ag-grid from trying to move away from the cell.
I tried loads of approaches to solving this - using the tabToNextCell events, suppressKeyboardEvent, navigateToNextCell, onCellEditingStopped - to name a few - this was the only thing that got it working correctly.
Here's my value parser, for what it's worth:
var codeParser = function (args) {
var cellEditor = _controller.currentCellEditor.children['codeValue'];
var paycodeId = +args.colDef.field;
var paycodeInfo = _controller.paycodes.filter(function (f) { return f.id === paycodeId; })[0];
// Check against any mask
if (paycodeInfo && paycodeInfo.mask) {
var reg = new RegExp("^" + paycodeInfo.mask + '$');
var match = args.newValue.match(reg);
if (!match) {
$mdToast.show($mdToast.simple().textContent('Invalid value - does not match paycode format.').position('top right').toastClass('errorToast'))
.then(function(r) {
_controller.currentCellEditor.children['codeValue'].focus();
});
throw 'Invalid value - does not match paycode format.';
}
}
return true;
};
The _controller.currentCellEditor value is set during the init of the cell editor component. I do this so I can then refocus the control after the error has been shown in the toast:
CodeValueEditor.prototype.init = function (params) {
var form = document.createElement('form');
form.setAttribute('id', 'mainForm');
form.setAttribute('name', 'mainForm');
var input = document.createElement('input');
input.classList.add('ag-cell-edit-input');
input.classList.add('paycode-editor');
input.setAttribute('name', 'codeValue');
input.setAttribute('id', 'codeValue');
input.tabIndex = "0";
input.value = params.value;
if (params.mask) {
input.setAttribute('data-mask', params.mask);
input.setAttribute('ng-pattern','/^' + params.mask + '$/');
input.setAttribute('ng-class',"{'pattern-error': mainForm.codeValue.$error.pattern}");
input.setAttribute('ng-model', 'ctl.currentValue');
}
form.appendChild(input);
this.container = form;
$compile(this.container)($scope);
_controller.currentValue = null;
// This is crucial - we can then reference the container in
// the parser later on to refocus the control
_controller.currentCellEditor = this.container;
$scope.$digest();
};
And then cleared in the grid options onCellEditingStopped event:
onCellEditingStopped: function (event) {
$scope.$apply(function() {
_controller.currentCellEditor = null;
});
},
I realise it's not specifically for your components (Vue.js) but hopefully it'll help someone else. If anyone has done it a better way, I'm all ears as I don't like throwing the unnecessary exception!

how to do different tooltips depending on data with Meteor Blaze and Bootstrap?

I'm using Meteor Blaze and Bootstrap 3 to display an img with a tooltip on mouseover. It works fine with a static tooltip text:
<img class="socialMediaIcon" src={{iconPath}} data-toggle="tooltip"
data-placement="right" title={{tooltip}} />
but I want to dynamically change the tooltip depending on the value of a Collection document field.
I've created a template helper to generate the desired text:
Template.SocialMedia.helpers({
getSocialMediaIconTooltip: function(service) {
console.log(">>>>>> Tooltip service =", service);
var smsdata = socialMediaSystem.findOne({service: service});
if (!smsdata.active)
return smsData.tooltip;
else {
var smudata = socialMediaUser.findOne({accountId: Meteor.user()._id, service: service});
if (smudata)
return "Disconnect " + smsData.tooltip;
else
return "Connect " + smsData.tooltip;
}
},
and I'm calling it with:
<img class="socialMediaIcon" src={{iconPath}} data-toggle="tooltip"
data-placement="right" title={{getSocialMediaIconTooltip name}} />
where "name" is a field in the open document (the code is inside a {{#each}} loop). "name" is non-blank and is used successfully later in the #each block. "getSocialMediaIconTooltip" never gets called and no tooltip appears. I've used this argument passing syntax in other places that work. What am I doing wrong?
The helper might be throwing an error, and in Blaze templates this means blank output (i.e. no tooltip). Check the browser console for error messages.
Your helper refers to both smsdata and smsData. Is that correct, or is smsData an invalid reference?
Typos on my part, see comment above. The corrected code for reference:
Template.SocialMedia.helpers({
getSocialMediaIconTooltip: function(service) {
try {
const smsdata = socialMediaSystem.findOne({subname: service});
if (smsdata.active == false)
return smsdata.tooltip;
else {
const smudata = socialMediaUser.findOne({accountId: Meteor.user()._id, service: service});
if (smudata)
return "Disconnect " + smsdata.tooltip;
else
return "Connect " + smsdata.tooltip;
}
} catch(ex) {
console.error(ex);
}
},

3 way binding using firebaseObject opened in input fields

Hi I'm trying to make 3 way binding work on objects opened in input fields (Doesn't have to be Object)
The idea is to open an object in a div with the results of the object inside input fields, and to change the values of that object one would just need to change the data in those fields and the changes would be made to the objects values directly.
Anyone done something like this?
EDIT
I have done the simpler guides on firebase.com but for my project there is several fields on the object, I deleted the code I tried with but it was something similar to this.
Controller
$scope.loadModel = function($id) {
var rec = $firebaseObject(new Firebase('https://****.firebaseio.com/models/' + $id));
rec.$loaded().then(function(rec){
var record = rec;
$scope.record = record;
console.log(record.name);
var child = rec.client.id;
record.$bindTo($scope, "record").then(function() {
console.log($scope.data);
$scope.data.foo = "baz";
record.set({ foo: "baz" });
});
var obj = $firebaseObject(new Firebase('https://dam-db.firebaseio.com/clients/'+child));
obj.$loaded().then(function(obj){
$scope.object = obj;
console.log(obj.name);
//return obj;
})
})
};
Html
<div ng-model="record" ng-repeat="record in record">
<input value="{{record}}"></input>
</div>

Google Script - Adding dynamic parameters to href from handler

I have a Google Script published as a web app which uses UI service to display an interface with several listboxes. I can get at the values selected thru server handlers.
My problem is that I need to add these values to a url in a anchor defined in my doGet routine. (I am calling a JotForm url, and need the dynamic parameters to pre-populate the form)
I can't see how to modify the anchor from the handler function, or any other way to invoke the url I build in code.
When you want to modify any widget in a Ui created with UiApp, each widget must have an ID that you can use to getElementById() and manipulate the way you want just as if you were in the doGet function.
Here is a simple example to illustrate : (online here)
function doGet(){
var app = UiApp.createApplication().setTitle('test');
var serieNames = [' serie A',' serie B',' serie C'];
var panel = app.createVerticalPanel().setStyleAttribute('padding','30px');
var namesHandler = app.createServerHandler('showPilots').addCallbackElement(panel);
for(var n in serieNames){
var serieSelect = app.createRadioButton('pilotSelect',serieNames[n]).setId('s'+n).addClickHandler(namesHandler)
panel.add(serieSelect);
}
app.add(panel);
return app;
}
function showPilots(e){
Logger.log(JSON.stringify(e));// you can see the source parameter in e that returns the widgets ID of the button you clicked to call the handler
var app = UiApp.getActiveApplication();
var serie = e.parameter.source; // get the ID
app.add(app.createLabel('you clicked '+e.parameter.source));// then get this widget by its ID and modify it
app.getElementById(serie).setText('Clicked');// modify it
return app;// update Ui
}
EDIT : here is a version that manipulates anchors, it is perfectly possible to change the url from a handler.
test here
code :
function doGet(){
var app = UiApp.createApplication().setTitle('test');
var links = ['link 1',' link 2',' link 3'];
var linkshref = ['http://www.google.com','http://www.packtpub.com/google-apps-script-for-beginners/book','http://stackoverflow.com/questions/tagged/google-apps-script'];
var panel = app.createVerticalPanel().setStyleAttribute('padding','30px');
var namesHandler = app.createServerHandler('changeUrl').addCallbackElement(panel);
for(var n in links){
var linkWidget = app.createAnchor(links[n], linkshref[n]).setId('s'+n);
panel.add(linkWidget);
}
var btn = app.createButton('change links',namesHandler);
app.add(panel.add(btn));
return app;
}
function changeUrl(e){
Logger.log(JSON.stringify(e));// you can see the source parameter in e that returns the widgets ID of the button you clicked to call the handler
var app = UiApp.getActiveApplication();
var links = ['New link 1','New link 2','new link 3'];
var linkshref = ['http://www.microsoft.com','http://www.w3schools.com/js/','https://sites.google.com/site/appsscriptexperiments/'];
for(var n in links){
app.getElementById('s'+n).setHref(linkshref[n]).setHTML(links[n]);
}
return app;// update Ui
}

Dojo: Can't update drop down list after adding a new group

I've been playing around with IBM's tutorial at this link.
http://www.ibm.com/developerworks/web/tutorials/wa-dojotoolkit/section6.html
I've done very well so far, but I can't seem to get the drop down list to populate the new group entry. Even the original code isn't working.
//Refresh the data store for the groups dropdown (in case groups added, edited or deleted)
function refreshGroupDropDown() {
var theStore = dijit.byId("edit_contact_group").store;
theStore.close();
theStore.url = "data/groups.php";
theStore.fetch();
}
Thanks!
Update: Still having trouble. I tried this below and still nothing. The function refreshGroupDropDown() is called when the user opens the edit contact windows or new contact window.
//Refresh the data store for the groups dropdown (in case groups added, edited or deleted)
function refreshGroupDropDown() {
var new_store = new ItemFileReadStore({url: 'data/groups.php' , clearOnClose: true});
var theStore = dijit.byId("edit_contact_group");
theStore.store = new_store;
theStore.close();
theStore.fetch();
}
//Clears the "Edit Contact" form, sets it up for adding a new contact
function newContact() {
var contact = contactsGrid.selection.getSelected()[0];
refreshGroupDropDown();
dojo.byId("edit_contact_real_id").value = "";
dojo.byId("edit_contact_id").value = "[NEW]";
dijit.byId("edit_contact_group").reset();
dijit.byId("edit_contact_first_name").reset();
dijit.byId("edit_contact_last_name").reset();
dijit.byId("edit_contact_email_address").reset();
dijit.byId("edit_contact_home_phone").reset();
dijit.byId("edit_contact_work_phone").reset();
dijit.byId("editContactDialog").set("title", "New Contact");
dijit.byId("editContactDialog").show();
}
//Process the adding of a new group to the database
function doNewGroup(e) {
e.preventDefault();
e.stopPropagation();
dojo.byId("new_group_ajax").value = "1";
if(this.isValid()) {
dojo.xhrPost({
form: this.domNode,
handleAs: "json",
load: function(data) {
if(data.success) {
okDialog.set("title","Group created successfully");
okDialogMsg.innerHTML = "The group <strong>"+data.name+"</strong> was created successfully.";
groupsStore.newItem({"id":data.id.toString(),"name":data.name}, {"parent": groupsModel.root, "attribute":"groups"});
groupsStore.save();
newGroupDialog.hide();
okDialog.show();
}
else {
okDialog.set("title","Error creating group");
okDialogMsg.innerHTML = data.error;
okDialog.show();
}
},
error: function(error) {
okDialog.set("title","Error creating group");
okDialogMsg.innerHTML = error;
okDialog.show();
}
});
}
}
Hopefully this helps! I'm a beginner so any help is appreciated.
I figured it out! The issue was with the index.html. The input tag for the groups drop-down list looks like this
<input dojoType="dijit.form.FilteringSelect" name="move_contact_new" store="groupsStore" searchAttr="name" query="{type:'node'}" id="move_contact_new" required="true" style="margin-bottom: 6px" />
The query attribute was never set correctly. Once I deleted query="{type:'node'}" the groups re-populate after adding, editing, or deleting groups.
A beginner answer for a beginner question.
Hope this can help any beginners out there.
Based on what you've posted, the only problem I see is with the line var theStore = dijit.byId("edit_contact_group").store;because it doesn't acutally create a dataStore. You need to make sure you also include something like `var edit_contact_group = new dojo.data.ItemFileReadStore();or an equivalent. Othewise, have you connected the refreshGroupDropDown() function to the appropriated event ('onclick' or whatever) using dojo.connect()? Have you loaded the function refreshGroupDropDown() using dojo.ready? ie. dojo.ready(function(){refreshGroupDropDown();});Those are always the first things that come to mind...