I have an input field with a value binding. The binding references a writable computed observable.
The intended behavior is:
a user types some letters: The writable computed should recognize the user is still typing
a user finishes typing: The writable should save the value
However, I only can choose updateValue: 'input' or updateValue: 'keydown', but not both. In the JavaScript code, the writable computed is not able to check which event occurred.
I need this behavior for this reason: if the user is just typing I want to fill an auto-suggestion box and want to bold the already typed letters. For this I need the keydown event. But if the user finishes typing (losing focus) I want to do some verification and then save the entered value.
If I use input the verification is working but the auto-suggestion feature is broken. If I use keydown the user isn't able to type something, because the verification always fails and the user cannot finish typing.
Suppose you have computed named self.inputValue which references to the input field.
You can apply rateLimit extender on perticular computed..
e.g.
self.inputValue = ko.computed(function(){
//perform the required logic
}).extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 400 } });;
where timeout is in miliseconds. It will hold the updation or notification of value for 400 miliseconds.
This should solve your problem. A small JSFiddle I've made for you : http://jsfiddle.net/rahulrulez/x8jmcpLh/
rateLimit was introduced in KO 3.1.0, if you are using old library then use throttle instead.
Related
Just the background:
(not really related to the question)
I am using SharePoint and creating a Sharepoint Hosted App.
I have some fields which provide a peoplepicker with which I can select Users out of a user pool. The functioninality is provide by the SharePoint internal scripts.
If I input a name of a user via jquery nothing happens. It has no built-in change handler I guess.
What I have done is a separat script which just resolve the user names on my jquery input via trigger("change") and then the SharePoint internal Scripts doing the job. This "job" is asynchronous and then if the data arrives from the backend the SharePoint Scripts apply the data to the Peoplepicker fields.
But this functions also doesn't support jQuery's trigger because I cannot alter the functions as they're pre-defined...
So want I want to do is to monitor when the text inside the Peoplepicker fields has been changed but I am a little bit lost now.
Question:
I have tried it with the following code (which doesn't work):
$('.peoplePickerDiv').bind("DOMSubtreeModified", function () {
alert("HAS BEEN CHANGED!");
});
Is there another way to react on lets say background editing of input fields in my DOM?
For People Picker control manipulation SPClientPeoplePicker object (clientpeoplepicker.js) is intended, which provides methods for getting information from the picker or to perform other operation.
SPClientPeoplePicker class exposes the following events:
OnControlValidateClientScript - triggers after a server error is set or cleared
OnUserResolvedClientScript - triggers once the resolved user is added or removed in client
OnValueChangedClientScript - triggers after text input or users change in client
OnValueChangedClientScript event probably suits your scenario, here is an example on how to attach it:
var picker = SPClientPeoplePicker.SPClientPeoplePickerDict[peoplePickerId];
picker.OnValueChangedClientScript = function (elementId, userInfo) {
if(userInfo.length > 0){ // once the value is resolved it could be retrieved via `userInfo` object
console.log(userInfo[0]);
}
console.log('value changed');
};
References
Use the client-side People Picker control in SharePoint-hosted SharePoint Add-ins
In Dynamics CRM 2013 is it possible to revert a field changed on a form when a business process error occurs?
For example:
1. User changes a text field on a form from 'abc' to 'xyz'
2. User clicks save
3. CRM pre-operation plugin validates field, 'xyz' not allowed, exception thrown
4. CRM displays business process error to user, 'xyz' is not allowed
5. The value 'xyz' is still shown in the form
The desired behavior we want is 'xyz' to revert to 'abc' in step 5.
You will need to cache the data first. You can do this OnLoad, e.g. by memorizing the entity's attribute values:
function GetInitialAttributeState() {
var preImage = {};
Xrm.Page.data.entity.attributes.forEach(function(field) {
// TODO: for lookup attributes you need to do extra work in order to avoid making merely a copy of an object reference.
preImage[field.getName()] = field.getValue();
});
return preImage;
}
window.preImage = GetInitialAttributeState();
Then you need to perform the save operation through the Xrm.Page.data.save method. Pass a callback function handling errors and resetting the fields, e.g.
Xrm.Page.data.save().then(
function() {
/* Handle success here. */
window.preImage = getInitialAttributeState();
},
function() {
/* Handle errors here. */
Xrm.Page.data.entity.attributes.forEach(function(field) {
if (field.getIsDirty()) {
field.setValue(preImage[field.getName()]);
}
});
});
It is not possible to reset the form's fields this way using the save event, because it kicks in before the actual save operation, never after it.
Why let the user save the record at all?
You could use a Business Ruel to validate the field, and set an error condition against the field for values you don't "like". The error condition will persist and prevent them saving the record until they change the value. The error message can give them some explanation as to why their value is not valid.
Obviously the validation you can do in a Business Rule is limited, but your example does not make it clear on what basis we match "xyz" as a "bad" value.
I'm using bootstrap Tokenfield library. I have a case in my project saying that I can edit tokens by clicking, but not typing, I have special form for adding new pills.
Is there a way to disable typing in input form, but still have an access to removing and adding pills by clicking?
You can set showAutocompleteOnFocus for mouse focus so there is not any need to use keyboard for add token or remove token, example code :-
$('#tokenfield').tokenfield({
autocomplete: {
source: ['red','blue','green','yellow','violet','brown','purple','black','white'],
delay: 100
},
showAutocompleteOnFocus: true
});
$("#tokenfield").keydown( function(key) {
return false;
});
I had a similar requirement in a project I'm working on, all the tokens are defined using a form, our requirement dictate that the user could remove a token, but not edit or add new tokens in the input, only using the provided form. My "solution" was a bit hacky: bootstrap-tokenfield adds a new input in the DOM for the user to type text that is ultimately transformed into tokens, you can disable this input and effectively prevent the user for typing new tokens.
By default the new token receive an id with the format id-tokenfield where id is the id of the original input. If no id es provided on the original input then a random number is used instead.
So disabling this input:
$('#search-tokenfield').prop('disabled', 'disabled');
Prevents the user from creating new tokens, while keeping the chance to edit/delete tokens. In my case search is the id of the original input.
You can disable the input with id "tokenfield-tokenfield" after the token is created, and enable it back when it is removed.
$('#tokenfield').on('tokenfield:createdtoken', function (e) {
$('#tokenfield-tokenfield').prop('disabled', true);
});
$('#tokenfield').on('tokenfield:removedtoken', function (e) {
$('#tokenfield-tokenfield').prop('disabled', false);
});
In Tapestry 5, in a form, I need to log (via google analytics) when each individual form field has been completed and passed the client-side validation successfully.
I need to do this for each individual field in real-time, even if the form is not submitted, so waiting until form submission and doing this on the server is not an option.
Is there a way (out of the box) to hook into the success/failure of the javascript client-side validation provided by Tapestry 5?
The two possibilities I can think of would be:
A Tapestry api to provide a callback function to the success/failure events.
Listening for some Tapestry custom event on success/failure.
But I cannot find anything in the documentation about either of these existing. Is either of these options possible, or is there another way to achieve this?
You might be able to attach clientside event listeners to achieve what you want. Take a look at Tapestry.FormEventManager.handleSubmit in tapestry.js. Form submission fires the following clientside events:
/**
* Event that allows observers to perform cross-form validation after
* individual fields have performed their validation. The form element is
* passed as the event memo. Observers may set the validationError property
* of the Form's Tapestry object to true (which will prevent form
* submission).
*/
FORM_VALIDATE_EVENT: "tapestry:formvalidate",
/**
* Event fired just before the form submits, to allow observers to make
* final preparations for the submission, such as updating hidden form
* fields. The form element is passed as the event memo.
*/
FORM_PREPARE_FOR_SUBMIT_EVENT: "tapestry:formprepareforsubmit",
/**
* Form event fired after prepare.
*/
FORM_PROCESS_SUBMIT_EVENT: "tapestry:formprocesssubmit",
/**
* Event, fired on a field element, to cause observers to validate the
* input. Passes a memo object with two keys: "value" (the raw input value)
* and "translated" (the parsed value, usually meaning a number parsed from
* a string). Observers may invoke Element.showValidationMessage() to
* identify that the field is in error (and decorate the field and show a
* popup error message).
*/
FIELD_VALIDATE_EVENT: "tapestry:fieldvalidate",
/**
* Event notification, on a form object, that is used to trigger validation
* on all fields within the form (observed by each field's
* Tapestry.FieldEventManager).
*/
FORM_VALIDATE_FIELDS_EVENT: "tapestry:validatefields",
A colleague suggested overriding the Tapestry.FieldEventManager.validateInput function using ProptypeJS Class.create.
Tapestry.FieldEventManager = Class.create(Tapestry.FieldEventManager, {
validateInput : function($super) {
var isValidationFailure = $super();
// can use this.field inside this function to access the field being validated
if(!isValidationFailure) {
// LOGIC ON VALIDATION SUCCESS
} else {
// LOGIC ON VALIDATION FAILURE
}
return isValidationFailure;
}
});
This solves the problem nicely, allowing for clean readable code and easy access to both the super implementation and the actual field itself, which I needed to do my tracking logic. And the best part is it just works, with validateInput being called on each individual form field when it loses focus. No detailed knowledge of, or messy code around, the events that drive the validation engine required!
FYI validateInput will also run for each form field on form submission - that can be worked around by overriding Tapestry.FormEventManager.handleSubmit to hook into form submission logic... but that is a little beyond the scope of this answer.
Hope you can assist.
I am currently trying to conduct one of the most simplest tasks via a user event script - that is to set a new value in the 'discount rate' field on the sales order. My script works fine when testing it on the client, but when the scheduled script is triggered, the field fails to set/update.
The following code is within a 'beforesubmit' operation. Can you spot what I have done wrong?
function beforeSubmit_discountVAT(type){
if(nlapiGetContext().getExecutionContext() !='scheduled')
return;
var getDiscountVal = nlapiGetFieldValue('discountrate');
var correctDiscount = getDiscountVal / 1.2;
nlapiSetFieldValue('discountrate', correctDiscount);
}
In short - All i want to do is deduct the discount value by 20%. Can you use 'nlapiSetFieldValue' when a user event script is triggered from a scheduled script?
Thanks in advance.
AWB
When editing an existing record, nlapiSetFieldValue cannot be used in a Before Load event on fields that are stored with the record. From the function's JSDocs:
Sets the value of a given body field. This API can be used in user event beforeLoad scripts to initialize field on new records or non-stored fields.
nlapiSetFieldValue can thus only be used reliably in Before Load on new records or non-stored fields.
Realizing this is a month old so you've probably found a solution, I would move your code to the Before Submit event. I tested this using a Scheduled Script:
var customer = nlapiLoadRecord('customer', '31294');
nlapiSubmitRecord(customer);
and User Event on the Customer record, Before Submit event:
if (nlapiGetContext().getExecutionContext() === 'scheduled') {
nlapiSetFieldValue('url', 'http://www.google.com/', false);
}
This works as expected for me in a 2013.1 Sandbox environment.
Using nlapiSubmitField in After Submit as mentioned in the other answer is an unnecessarily long operation that will use extra governance units. Not a huge deal if that's the only thing your script is doing, but if you ever expand the script or add looping, it can add up quickly in terms of performance and governance usage.
Also, it may not be necessary, but you should ensure that getDiscountVal is a Number by doing:
var getDiscountVal = parseInt(nlapiGetFieldValue('discountrate'), 10);
If it comes back as a String then your division operation may give a strange result which may also cause nlapiSetFieldValue to fail or set the field to an odd value.
two suggestions
Make sure that your script is being executed by adding a few nlapiLogExecution statements
Instead of doing it in beforesubmit, change this field in aftersubmit function using nlapiSubmitField
"Can you use 'nlapiSetFieldValue' when a user event script is triggered from a scheduled script?"
Yeah Absolutely yes.Context is also correct its scheduled.
Please make sure that you are submitting the record nlapiSubmitRecord(recordObj) at the end.
If you are not comfortable with nlapiSubmitRecord() you can obviously use nlapiSubmitField()
If still you get stuck up then please paste the complete code so that we could assist you.
Cheers!!!