I have an app where you can enter some information about a customer. I have the problem that this information easily can be overwritten by other changes in the app.
I want:
A textarea showing the information
The message shown should update as changes are made to it in other places in the app
If the user has changed the message in the textarea, the message in the textarea should NOT be overwritten by server side changes or other changes in the app
When a save button is pressed, the model is updated
Simply doing this:
<textarea ng-model="customer.info"></textarea>
<button ng-click="save(customer)">Save</button>
won't work, because if something is changed to the customer object on the server, the information field will be reset and the user's changes will be overwritten.
Update:
In response to your comment/updated question: I'd simply track the state of the textarea using a couple of event listeners, that check if the user changed the value. If so: the textarea is no longer kept in sync with the server data.
I also check if the textarea has focus, if that is the case, the value should not be updated, because the user might be editing its contents.
Lastly, I've attached a blur event handler, that checks the value again, in case the user "undoes" changes. If a user adds a char by accident, and removes it later on, the textarea's value might be the same as the last known value that came from the server, in which case, the textarea's value should be kept in-sync again.
I've set up a simple fiddle that uses an interval to do all this. Replace the interval-related code with your worker, ajax callback or whatever it is you're using. If it's a worker, unsetting the interval should be replaced with unbinding the onmessage handlers with a handler function that syncs the textarea or doesn't sync the textarea. Alternatively use a single function that checks the changed flag whenever it wants to change the textarea's value. Plain and simple.
Here's the fiddle
And here's the code (the fiddle version contains some actual comments that explain various bits and pieces):
(function (txt)
{
var initialValue = txt.value,
changed = false,
id,
callback = function()
{
if (changed === false)
initialValue = (txt.value += ' Server-sync');
else
{
clearInterval(id);
id = null;
}
};
txt.addEventListener('change', function()
{
if (this.value !== initialValue)
changed = true;
}, false);
txt.addEventListener('focus', function()
{
changed = true;
}, false);
txt.addEventListener('blur', function()
{
if (this.value !== initialValue)
changed = true;
else
{
changed = false;
id = setInterval(callback, 5000);
}
}, false);
id = setInterval(callback,5000);
}(document.querySelector('#foo')));
Initial answer
Like I said in my comment:
<textarea ng-model="customer.info" id='foo' readonly></textarea>
prevents the value of the textare from being altered by the user, but you can still set/change its value in your JS code:
var changeTxt = (function(txt)
{
return function(addVal)
{
txt.value += addVal;
};
}(document.querySelector('#foo')));
Demo
To keep the value of the textArea in sync, you could perform an AJAX call prior to making any changes to the value client side, or create a worker "thread" that polls the server for changes that have been made since the value was last fetched.
Seeing as you're using angular.js, I must admit that I don't have any experience with how angular can be best used to solve your issue, but a quick google search lead me to 3-way data binding, which does look rather promising.
Related
I have a scheduler with custom events. In the edit event popup, there is a cancel button. The scheduler is from a javascript library, so not of my creation, I only created its custom functionality.
So in the edit popup (which triggers edit event), I set a variable to hold the current state of the event data. Then if I make any changes and press cancel instead of save, the cancel event is triggered. In the cancel event, the 'currentState' event data is now changed to the new state of all the changed data, which I don't want. I need the old data so I can revert it all back, then refresh scheduler.
This is my cancel event:
cancel: function(e){
console.log('Cancelling', e);
var kendoEvent = new kendo.data.SchedulerEvent();
var schema = e.sender.dataSource.options.schema.model.fields;
console.log(e.sender.dataSource.hasChanges());
console.log(currentEventState);
$.each(schema, function(index, value){
//console.log(value);
console.log(index);
kendoEvent[index] = e.sender.oldEventData[index];
if(index == 'ownerId'){
kendoEvent.ownerId = currentEventState.ownerId[0].value;
}
});
console.log(kendoEvent);
console.log(getIndexById(kendoEvent.taskId));
eventData[getIndexById(kendoEvent.taskId)] = kendoEvent;
e.sender.dataSource.read();
if(e.event.id != '0'){
dontUpdate = 1;
//e.sender.dataSource.sync();
}
},
Where e.sender is the scheduler, and e.event is the event data (with any changes that were made). In the edit event the first thing I do is add oldEventData field to e.sender and set it to e.event. But... if changes are made to e.event AFTER e.sender.oldEventData is set, e.sender.oldEventData STILL gets changed to reflect all the changes made...
How might I preserve this old data so I can revert it back? I tried just doing a global variable instead of creating a new field in e.sender, but after setting the global variable to e.event, it still reflected any changes made >:(.
Discovered that cloning the data into the field e.sender.oldEventData prevented it from being tied to e.event. So e.event could change without messing with the old data.
This was achieved with a simple JSON.parse(JSON.stringify(e.event)).
I attempted Object.freeze(e.sender.oldEventData), which kept the old data, but made it completely unable to modify at all, so wasn't the best solution for me.
I am writing javascript code to change the form of a entity in Dynamics CRM based on the value of a field on each form.
To change the form, the user has to change the value of the field.
Then during the onChange event, my js comes in, triggers saving, has to wait for the result and then change the form. (If you save and change at the same time, there is still a window shown asking the user to confirm leaving unsaved changes)
Now there should be a way to do that:
Xrm.Page.data.save(saveOptions).then(successCallback, errorCallback)
as it is described on msdn:
Saves the record asynchronously with the option to set callback functions to be executed after the save operation is completed.
I am using it as such:
var campaignType = Xrm.Page.getAttribute('typecode').getValue();
if (xxx.Forms.hasOwnProperty(campaignType)) {
Xrm.Page.data.save().then(function () { xxx.redirectToForm(xxx.Forms[campaignType]); }, null);
But the form change is still triggered immediately during the save.
What am I doing wrong?
I faced a similar problem while trying to update the process bar.
Xrm.Page.data.save().then
(function () {
window.location.reload(true);
},
function () {
windows.alert("broken");
}
);
I strongly suggest you to try to apply the logic on a vanilla CRM, for me what was breaking the logic was a third party component called N52 Rules, their code was interfering with the callback forcing the refresh of the page before the save event. Your code seems correct.
Hey the Save and Refresh Calls are Asynchronous! that is why it hits the success handler immediately.
What you can try is using SDK.REST.js file for CRM
function updateFunction(entityId) {
var campaignType = Xrm.Page.getAttribute('typecode').getValue();
if (xxx.Forms.hasOwnProperty(campaignType)) {
var entity= {};
entity.typecode= campaignType;
SDK.REST.updateRecord(
entityId,
entity,
entityName, //"Account"
function () {
writeMessage("The record changes were saved");
xxx.redirectToForm(xxx.Forms[campaignType]);
},
null
);
}
}
https://msdn.microsoft.com/en-us/library/gg334427(v=crm.7).aspx
Here you can call updateFunction given above onChange and in the onSuccess handler you can try calling the form you want to call. I haven't tried it the way you want, but let me know if it works.
check this link out as well
https://msdn.microsoft.com/en-us/library/gg334720.aspx#BKMK_entityOnSave
My Code is as below.
$(document).ready(function($)
{
var form = $("#video_detail_form");
var name = $("#videoTitle");
var nameInfo = $("#valid_videoTitle");
function validateName(){
//if it's NOT valid
var titleValue=$.trim(name.val());
if(titleValue.length == 0){
nameInfo.text("Please Enter Title");
return false;
}
//if it's valid
else{
nameInfo.text("");
return true;
}
}
name.blur(validateName);
name.keyup(validateName);
name.change(validateName);
$('#editVideoCancel').click(function(){
cancelVideoDetailAjaxCall( '/video/cancelVideoDetail', callBackCancelVideoDetail);
});
});
My cancelVideoDetailAjaxCall function changes text of the videoTitle input box. But my this code is not capturing that event by name.change.
If I change manually then it captures it. So when dynamically my callback function is changing the text then change event is not capturing it.
How should I capture that change?
You can actually extend your value change catching to all changes coming from some script using the jQuery val method, by setting a custom setter in jQuery.valHooks.
Imagine you change the input type to myCustomType, then you will implement jQuery.valHooks.myCustomType.set method which will be called each time val is used to set the input value, and you will include your specific call here. But I insist : it is not a best practice.
You will surely find explicit code on the web for that hooks.
As comments have mentioned, if you programmatically change the value via jQuery you must also trigger that change programmatically, if you want anything subscribed to it to register that change.
You can always make up your own events and trigger them accordingly if you don't want to "interfere" with other things already subscribed to regular events:
$el.on('mycustomevent', function() { ... })
$el.trigger('mycustomevent');
You can even subscribe with the same callback for the 'regular event' and your 'custom event':
$el.on('change', myChangeCallback);
...
$el.on('mycustomevent', myChangeCallback);
If you don't want to keep typing $el.val('...').trigger('mycustomevent') repeatedly, then declare a helper function that does that for you:
// helper function for changing the value
function changeInput(newVal) {
if(!this.$target) this.$target = $('#text'); // stash the target for reuse
this.$target
// programmatically change the value; does not fire 'change' event
.val(newVal)
// now trigger your custom action that behaves
.trigger('customaction'); // add extra parameters, etc
}
Full example: https://jsfiddle.net/drzaus/ds6g745s/8/
I've written a script for my registration form that if there's a keyup event on the username input field, then javascript checks if the username entered is valid and if it's then an ajax function is called to check its availability.
It's working fine. But username can be entered without doing a key up, what to do for those cases? E.g. username can be entered by clicking the input field and selecting a name suggested by the browser, Or by using an auto-form filler. There's no "keyup" in these cases, so what to do for these cases?
And if I am missing some case then pls tell.
Bind the callback to the change event too.
Instead of $('username_input').keyup or $('username_input').bind('keyup', callback) use
$('username_input').bind('keyup change blur', function () {
//
})
[UPDATE]
If you are not using jQuery try
function checkUserName() {
// Ajax validation code
}
userinput = document.getElementById('yourusernameinputid');
userinput.onclick = userinput.onchange = userinput.onblur = checkUserName;
on a side note, you should try learning jQuery. It could save you a lot of time, and help you make better sites in less time.
[UPDATE 2]
It looks like there is no way to detect change via autofill using events. You need to set a timer to automatically check your input at fixed interval and check with you last value.
(function() {
// It is always better to use a closure to prevent the value from
// getting overwritten by another piece of code
var value = '';
setInterval(2000, function() {
if (userinput.value != value) {
checkUserName();
value = userinput.value;
}
});
})();
Test the username when:
The blur event occurs (when the text field loses its focus), and
Before the user sumits the form, or
The change event occurs (I think this is the best).
setInterval is the only way.
Sources:
I want to trigger an event every single time data changes in an HTML text input field regardless of focus
jQuery: What listener do I use to check for browser auto filling the password input field?
I have a contact form that sends a value to a hidden input on successful completion of the sendmail function. I want to detect this value change and then use it to apply a class to a div/paragraph.
I asked a similar question recently and I'm aware that this requires the script to continually check the doc after DOM is loaded but even after adding .change() it just doesn't seem to want to add the class.
Here's the jQuery:
$(document).ready(function() {
$("#acf_success_sent").change(function(){
if ($("#acf_success_sent").val() == "1"){
$("#acf_verified").addClass('gone');
}
});
});
any help would be great. here's a link to a test version of form in case you're interested, everything works except the verified symbol doesn't disappear after a successful send http://seeshell.me/forms/contact.php
There'll be no "change" event fired when code updates the value of your <input> element, so the handler you've registered won't run. What you could do however is fire "change" from a watchdog:
var watchdog = setInterval(function() {
if ($('#acf_success_sent').val() !== originalValue)
$('#acf_success_sent').trigger('change');
}, 100);
How you set up "originalValue" depends on your application. You could, for example, keep a separate ".data()" value, and watch for whenever your saved value differs from the current "value" attribute. Or you could keep the value in a closure variable:
var watchdog = (function() {
var $acfSuccessSent = $('#acf_success_sent'), cachedValue = $acfSuccessSent.val();
return function() {
if (cachedValue !== $acfSuccessSent.val())
$acfSuccessSent.trigger('change');
};
})();