Displaying success/failure message on the form using dojo - javascript

I am creating a application using dojo 1.8.
I have a form with buttons to perform some action. Once the action is done, I get the status of the backend process execution. Currently I am using alert boxes to intimate user about the status. I know, it is very old fashioned. So what I am trying is: If the status is "success" then I will display a message (in green text) on the top of the form else the error message (in red).
For that in HTML file, I created two DIVs
<div id="successNotification" data-dojo-attach-point="successNotification"></div>
<div id="failureNotification" data-dojo-attach-point="failureNotification"></div>
and in the postCreate method, in requestCompleteCallback method of request.invokePluginService, I am trying to set the innerHTML of the DIV tag.
But below code always results in "successNotification is undefined" error.
if (ifSuccess == 'true' || ifSuccess )
{
var successNotification = dijit.byId("successNotification");
}
In the same block I have used dijit.byId("some other component") and that works fine.
what am I doing wrong?

I'm noticing several things here. First of all, your if code does not look valid (there's a ' after true).
Then, if you're creating custom widgets you should not retrieve widgets or DOM nodes by their ID, which means you should not use either:
dijit.byId()
dojo.byId()
dojo/dom::byId()
dijit/registry::byId()
The correct way is by using the attach points you created by using the data-dojo-attach-point attribute. When you inherit from the dijit/_TemplateMixin mixin, you can simply use: this.successNotification and if you inherit from dijit/_WidgetsInTemplateMixin as well, then you can use the same syntax to retrieve widget instances.
In your case it would be:
if (ifSuccess) {
var successNotification = this.successNotification;
}

Related

(JavaScript API 1.3 for Office) Set Value of Custom Properties

My client has decided to migrate to Office 2016 and porting portions of a business process to that client requires us to offer a replacement to the Document Information Panel, which is no longer available. The Backstage file information area isn't considered a sufficient user experience for the users in question, so we're endeavoring to replace the DIP with a Task Pane app.
This example: https://www.youtube.com/watch?v=LVGqpns0oT8&feature=share shows that the idea is, at least in theory, possible. We considered buying this app but can't find sufficient information to do so.
So we set about attempting to replicate the functionality we need in the DIP. It appears that we can successfully set Document Properties of standard types, such as strings, which looks something like this:
Word.context.run(function(context){
var properties = context.document.properties;
context.load(properties):
return context.sync().then(function(){
properties.title = properties.title + " Additional Title Text"; // once the sync goes off, this works.
return context.sync();
});
});
However, when we try to update an Document Property that's, for example, a Managed Metadata property defined by a SharePoint content type, the value in the proxy object loads and remains changed, but it seems to break its relationship to the actual document property. The code below demonstrates:
Word.context.run(function(context){
var properties = context.document.properties;
var customProperties = properties.customProperties;
context.load(properties):
context.load(customProperties);
return context.sync().then(function(){
var managedMetadataProperty = customProperties.getItem('MngdMetadata');
properties.title = properties.title + " Additional Title Text"; // once the sync goes off, this works.
context.load(managedMetadataProperty);
return context.sync().then(function(){
console.log(managedMetadataProperty.value) // let's say this looks like "10;#Label 1|64d2cd3d-57d4-4c23-9603-866d54ee74f1"
managedMetadataProperty.value = "11;#Label 2|cc3d57d4-4c23-72d4-3031-238b9100f52g"
return context.sync(); // now the value in the javascript object for managedMetadataProperty is updated, but the value in the document does not change.
});
});
});
The document property Managed Metadata Property never changes in the Word UI, nor does a change push back to the SharePoint. Say we save and close the document after making the update, then re-open it. The Property value has not visibly changed, however when we load the proxy object with 'context.load()', the value that's available reflects the changes we made on last run.
I'm unclear about why this would be. It seems like to circumvent this, I would need to make a call back to SharePoint to update the relevant field, but I don't know how I would instruct Word to refresh with the new information from SharePoint.
That's a great question.
The custom properties API gives you access to some built-in properties as well as custom properties. SP-related properties do NOT follow in this category from the API perspective. (and the same is true in VBA/VSTO/COM) To access those you need to use the CustomXmlParts functionalities. Here is a good example on how to use it in the Javascript API.
Also, FYI, the team is working right now in a feature to enable the DIP again, i don't have concrete dates or commitment, but you might get this functionality again out of the box soon.
Have you tried customPropertyCollectionObject.add(key, value) ?
It will replace existing kvp's in the customPropertiesCollectionObject.
Here is the documentation customPropertiesCollection

Unable to get property '0' of undefined or null reference Lookup field

Recently lookup field in dynamic CRM form started throwing this error:
"Unable to get property '0' of undefined or null reference"
when we tried to change this lookup field. There is no Javascript called on Onchange event
I have attached a screenshot of the error:
In this case, if I did not know where this setaddionalparams function is located, my first move would be to disable all (or one by one) custom events on the form in the Handler Properties dialog that is called when you double click on the event handler in the Form Properties dialog in the Events tab (this one). If the error stops appearing then obviously the function is somewhere in your code.
Good luck!
UPDATE
There can be more reasons why you still see this error, please check scripts attached to the Ribbon, scripts inside HTML Web Resources and IFrames if you have any.
In addition, it may not be a direct call to the attribute by name, it may be a for loop that iterates through all attributes in the form. In this case you will need to search the code by the following keyword getValue()[0]. It seems like someone accesses a lookup attribute without checking if it's null. It should be fixed like this:
var productId = null;
var lookupValue = Xrm.Page.getAttribute("productid").getValue();
if (!!lookupValue && lookupValue.length > 0){
productId = lookupValue[0].id;
}

Safe way to store data for button clicks

I am trying to find out what the safest way to store data for use when the user clicks on a button.
I know that you can store data in attributes(either the value attribute or a data- attribute) of the button tag like so:
<button type="button" value="1" data-value="1">Click me!</button>
But the problem with this is that the user(probably really only advanced users) can manipulate the value with firebug or some other app and THEN click the button and send over different data. I fully understand that I need to check the input before I try to do anything with the sent data.
I also found out that I could use jQuery's .data() to attach data to dom elements, which seems a bit more useful. I'm not exactly sure how the data is stored, but I assume its harder to manipulate.
What got me really interested in this question was when I was looking through Soundcloud's code in firebug, I saw that none of the "like" buttons had data attached to the buttons. I tried deleting/manipulating elements/data and the buttons still worked. So it got me thinking that they are probably using a similar process to what jquerys data() is doing.
I just want to know if there is a safer way to store data or a way so that the user can't manipulate the data before clicking the button.
Consider this function:
function setupPrivateData(element) {
var private = 1;
element.setPrivate = function ( d ) { private = d; }
element.getPrivate = function ( ) { return private; }
}
When called with some DOM element it will add two methods to it: .setPrivate(val) and .getPrivate().
These are the only methods that will allow you to access and modify that private variable associated with the element.
The user can always manipulate data. Nothing stops an advanced user to access object properties or call a jquery.data() on their own.
Something you could do in vanilla js would be:
var div = document.getElementById("test");
div.something = "hidden value";
div.addEventListener("click", function() {
alert(this.something);
});
<div id="test">click me</div>
The best way would to be a serverside verification if the sent data is valid or not.
Besides that, you could try to wrap your code in an anonymous function to deny the user access to the object:
(function() {
var data = {};
data.something = "test";
})()
But even that fails as soon as the user manipulates your files and adds for instance a debugger statement.
You can obfuscate your javascript but the only validation has to be done on your server. For example, I tried to get the weather from theweathernetwork. They have hidden their API call using multiple files and callbacks. In my opinion, it's just more challenging (funnier) if you want to reverse-engineer their site.
Javascript can't be secure. Never trust user input
If you are logging button clicks, the safest way to keep track is to save and validate on the server side.
For example, when you click a like button on Soundcloud, it makes an HTTP request to Soundcloud's server, records that you clicked the button, and marks it as a favorite. This way, if the same user clicks the button anytime in the future, it can check before incrementing the number of favorites.
The number displayed in the button is also pulled in from the database when the view is rendered.
This is a huge topic, and you have a lot to learn, far too much for a comment here. Anything "stored" in an attribute in the HTML source is absolutely not secure, it can be changed very very easily.
The most common way of dealing with this would be to use a cookie, but even with some effort these can be manipulated.
If security is important, find some way of identifying your users (possibly by IP, but even that isn't fool proof!) and keep the data on your server, linked to a user ID which can be retrieved after the button is clicked.

Conditionally attaching jQuery plugin functions to elements on the site

I want to attach a jQuery plugin function to an element on my site that exists only on one page. Currently, I'm using this conditional to prevent triggering the limiter function and throwing an error when there is no #advertiser_co_desc in view.
jQuery(document).ready(function($) {
var elem = $('#charNum');
if ($('#advertiser_co_desc').length) {
$('#advertiser_co_desc').limiter(180, elem);
}
});
On my website #advertiser_co_desc is present only on one page.
My solution does the job, but my qualm stems from the fact that the jQuery plugin code as well as the plugin function call presented above (they are both in the same file) get fetched by the browser and the condition is continuously evaluated regardless of whether a user ever gets to a page where #advertiser_co_desc exists.
Is the method I'm using optimal, or is there a better way to attach this particular JS only to the page where '#advertiser_co_desc` exists? Naturally, I wan to avoid adding my scripts in the same file with the PHP code.
Or you can wrap the plugin method as,
var _limiter = $.fn.limiter;
$.fn.limiter = function(limit, element) { // provide complete argmuments
if(this.length) {
_limiter.call(limit, element);
}
};
Make sure that plugin is loaded before this statements.
The best and optimal way to check existence of an element by jquery, is $('#advertiser_co_desc').length that you have used already. So no need to change your code.

custom error display with parsley.js 2.x

I need to show a list of validation errors in a popup.
I've disabled UI modification with <form data-parsley-ui-enabled="false"... and subscribed to the "parsley:field:error" event, where I collect error information, and then on "parsley:form:validated" I display the popup, ofcourse only on the condition isValid() == false.
But I am having problems with getting the actual error message in the "parsley:field:error" handler. The handler gets a single parameter containing an object, which so far I inspected to have several properties:
$element - the actual jQuery field,
constraints - list of constraints
options.i18n - it has some raw error message strings which I can get iterating with an n variable like so: obj.options.i18n.<LANGUAGE_CODE>.[obj.constraints[n].name], but they ocasionally contain placeholders (%s) and therefore are not suitable for display to the end
user; and sometimes there's an array instead of a single string, which defeats the idea completely;
The question is, how do I get the actual error message which would got displayed if I hadn't disabled the UI?
Solution
Use the following way to access a prioritized error message (i.e data-parsley-priority-enabled=true):
$.listen('parsley:field:error', function(parsleyField) {
// parsley field
console.log(parsleyField);
// which constraint has failed
console.log(parsleyField.validationResult[0].assert.name);
// the data-parsley-<constraint>-message
console.log(parsleyField.options[parsleyField.validationResult[0].assert.name+'Message']);
// the default constraint fail message
console.log(window.ParsleyValidator.getErrorMessage(parsleyField.validationResult[0].assert));
});
Short Explanation
You were almost there, the messages are stored in the options object itself, and the format for the message is like this: <constraint>Message, for example: requiredMessage.
Which is similar to the "data attribute to js variable conversion" convention like in jQuery, this has been mentioned in the docs: <parsleynamespace>-<constraint>-message becomes <constraint>Message.
Got this idea after seeing the annotated source for ui.js, check the _getErrorMessage function.
To access all the validation messages for a field on error (i.e data-parsley-priority-enabled=false), you can simply iterate through the
parsleyField.validationResult array:
for (i=0; i<parsleyField.validationResult.length; i++) {
console.log(parsleyField.options[parsleyField.validationResult[i].assert.name+'Message']);
}

Categories