knockout dirty flag code not working - javascript

Just started with knockout and need to implement page change warning. Following is the code snippet. I just need an alert pop up as warning if any change is made on the page.
function parseViewModel() {
var viewModel = JSON.parse(getState());
viewModel.checking = ko.observable(false);
viewModel.Slider = new ko.observable(100 - viewModel.Slider);
viewModel.CausalsList = buildHierarchy(viewModel.Causals);
viewModel.Causals["-1"] = "Total Marketing Budget";
viewModel.GeographiesList = ko.observableArray(gl);
viewModel.Geographies["0"] = "All Geographies";
viewModel.ProductsList = ko.observableArray(pl);
viewModel.Products["0"] = "All Products";
.
.
.
return viewModel;
}
function bindModel() {
model = parseViewModel();
ko.dirtyFlag = function (root, isInitiallyDirty) {
var result = function () { },
_initialState = ko.observable(ko.toJSON(root)),
_isInitiallyDirty = ko.observable(isInitiallyDirty);
result.isDirty = ko.computed(function () {
return _isInitiallyDirty() || _initialState() !== ko.toJSON(root);
});
result.reset = function () {
_initialState(ko.toJSON(root));
_isInitiallyDirty(false);
};
return result;
};
model.dirtyFlag = new ko.dirtyFlag(model);
model.isDirty.subscribe(function () {
alert("Page change warning!");
});
ko.applyBindings(model, $('#const').get(0));
ko.applyBindings(model, $('#buttonDiv').get(0));
}
Referred Ryan Niemeyer's blog. Unfortunately, it's not working anymore. Any insights please?

You would want to subscribe to model.dirtyFlag.isDirty in your case rather than model.isDirty.

One way to do is by using customBinding. I'm not that familiar with KO either but this might be something you're interested on.
Basically you would do is :-
ko.bindingHandlers.myFunction = {
update : function(){
//do something
}
}
http://knockoutjs.com/documentation/custom-bindings.html
And call it on your element using :-
<h1 data-bind="myFunction:{}"></h1>
Also, a jsfiddle to show how it works. (If you change the value of the First Name and focus out of it then the customBinding gets triggered. )
http://jsfiddle.net/3vuTk
Not sure if it's the best practice though.

Related

Handsontable, custom text editor, copy/paste issue

I have a table with two columns: name and code. I have created a simple custom editor for code column. The idea is, when user double clicks on the cell, the custom dialog with code editor opens. I have implemented it and posted the simplified example here:
Link to plunker
However, I have one issue with copy/paste functionality: if I use my editor, edit some code for the cell, press “Save”, the "code" column value seems to be correctly saved. But when I select this cell and press Ctrl+C, the value is not copied.
The question is: is it a bug in handsontable or I have just missed something, while implementing the custom editor? How should I change my custom editor to make the copy-paste functionality working properly.
The code of editor:
var ScriptEditor = Handsontable.editors.TextEditor.prototype.extend();
ScriptEditor.prototype.getValue = function () {
return this.TEXTAREA.value;
};
ScriptEditor.prototype.setValue = function (value) {
this.TEXTAREA.value = value;
};
ScriptEditor.prototype.open = function () {
var self = this;
this.instance.deselectCell();
var value = self.instance.getDataAtCell(self.row, self.col);
var decodedCode = decodeURI(value);
var success = function (resultCode) {
var encodedCode = encodeURI(resultCode);
self.instance.setDataAtCell(self.row, self.col, encodedCode, 'edit');
self.instance.selectCell(self.row, self.col);
};
openEditor(decodedCode)
.then(success);
};
ScriptEditor.prototype.focus = function () {
Handsontable.editors.TextEditor.prototype.focus.apply(this, arguments);
};
ScriptEditor.prototype.close = function () {
};
var openEditor = function (codeToEdit) {
var deferred = $q.defer();
var dialog = ngDialog.open({
template: 'editorTemplate.htm',
className: 'ngdialog-theme-default',
controllerAs: "editor",
controller: function () {
var vm = this;
vm.inputCode = codeToEdit;
vm.submitChanges = function () {
dialog.close();
deferred.resolve(vm.inputCode);
};
}
});
return deferred.promise;
};
Specs:
Angular version: 1.6.1
Handsontable version: 0.31.2
Chrome version: Version 58.0.3029.81
I have posted an issue on handsontable github repositiory and received there the answer. Link to issue: here
Solution:
Like the member of handsontable team has suggested, in open function before opening my dialog I call this.instance.deselectCell();. However, with this solution, the problem was, that if I press Enter my code editor dialog, not the new line is inserted, but the next cell in handsontable is selected. Then, I have wrappped the call in setTimeout() and it is worked.
Link to plunker:
here
The working code is:
ScriptEditor.prototype.open = function () {
var self = this;
setTimeout(function () {
self.instance.deselectCell();
});
var value = self.instance.getDataAtCell(self.row, self.col);
var decodedCode = decodeURI(value);
var success = function (resultCode) {
var encodedCode = encodeURI(resultCode);
self.instance.setDataAtCell(self.row, self.col, encodedCode, 'edit');
self.instance.selectCell(self.row, self.col);
};
openEditor(decodedCode)
.then(success);
};

Count not updating on click

I have two multiselect menus where I'm trying to get a total of how many children are present in each multiselct on load, then update the numbers, based on a click event which will push from one to the other, or vice versa.
The onload portion is working fine. I'm getting the results I'd expect and the counts are accurate.
The problem I'm having is updating both counts once the click event triggers. My counts never change.
Here's my code along with a fiddle:
var activeUser = $('.activeUsers');
var eligibleUser = $('.eligibleUsers');
var availableUserCount = $("#availableUsers option").length;
var eligibleUserCount = $("#eligibleUsers option").length;
activeUser.html(availableUserCount);
eligibleUser.html(eligibleUserCount);
$('#availableUsers').click(function () {
return !$('#availableUsers option:selected').remove().appendTo('#eligibleUsers');
activeUser.length(function() {
return availableUserCount();
});
eligibleUser.length(function() {
return eligibleUserCount();
});
});
$('#eligibleUsers').click(function () {
return !$('#eligibleUsers option:selected').remove().appendTo('#availableUsers');
activeUser.length(function() {
return availableUserCount();
});
eligibleUser.length(function() {
return eligibleUserCount();
});
});
http://jsfiddle.net/mujaji/8gkLyfe3/3/
What am I doing wrong?
There seems to be 3 problems with your code.
You are using return in the fist line of the click event. So the following code will never be executed (Get rid of that and only return if you cannot find any options)
There is no method called length for a div element. (Use .text() instead)
When you are returning the length inside the function return availableUserCount(); it will return you the cached value. (You need to reselect the element again)
So your code should technically look like this (further refactoring can still be made)
var activeUser = $('.activeUsers');
var eligibleUser = $('.eligibleUsers');
var availableUserCount = $("#availableUsers option").length;
var eligibleUserCount = $("#eligibleUsers option").length;
activeUser.html(availableUserCount);
eligibleUser.html(eligibleUserCount);
$('#availableUsers').click(function () {
!$('#availableUsers option:selected').remove().appendTo('#eligibleUsers');
activeUser.text(function() {
return $("#availableUsers option").length;
});
eligibleUser.text(function() {
return $("#eligibleUsers option").length;
});
});
$('#eligibleUsers').click(function () {
!$('#eligibleUsers option:selected').remove().appendTo('#availableUsers');
activeUser.text(function() {
return $("#availableUsers option").length;
});
eligibleUser.text(function() {
return $("#eligibleUsers option").length;
});
});
Check Fiddle
$("#availableUsers option").length doesn't dynamically change with the number of options. Once you set it up top, it's 40 forever. This does what you want:
$('#availableUsers').click(function () {
$('#availableUsers option:selected').remove().appendTo('#eligibleUsers');
activeUser.text($("#availableUsers option").length);
eligibleUser.text($("#eligibleUsers option").length);
});
Although it's not efficient to re-query every time when you could do
availableUserCount--; eligibleUserCount++;
And keep track of it manually.
Best solution (sic) :D
/*JQUERY FUNCTIONS*/
var activeUser = $('.activeUsers');
var eligibleUser = $('.eligibleUsers');
var eligibleUserCount = function(){eligibleUser.html($("#eligibleUsers option").length)};
var availableUserCount = function(){activeUser.html($("#availableUsers option").length)};
eligibleUserCount();
availableUserCount();
$('#availableUsers').click(function () {
$('#availableUsers option:selected').remove().appendTo('#eligibleUsers');
availableUserCount();
eligibleUserCount()
});
$('#eligibleUsers').click(function () {
$('#eligibleUsers option:selected').remove().appendTo('#availableUsers');
availableUserCount();
eligibleUserCount()
});
http://jsfiddle.net/8gkLyfe3/5/
Using return in the first line of the functions prevents any other code from executing in that block.
Check out my fiddle for a functionalized way to perform this
function setUserCounts(){
availableUserCount = $("#availableUsers option").length;
eligibleUserCount = $("#eligibleUsers option").length;
activeUser.html(availableUserCount);
eligibleUser.html(eligibleUserCount);
}
http://jsfiddle.net/8gkLyfe3/6/
Essentially, we add this function and then call it from within the click handlers, while also removing the

JavaScript function not giving the right output

This is a section of my view in which I want the result to show.
Can any one solve this problem?
When this.waitlist works, it should return the container like div#sntq-waitlist but it gives object[ ] instead.
Can anyone tell me why this is?
JavaScript
initLiveWaitList: function() {
this.waitlist_ = $('sntq-waitlist');
this.daily_status_ = $('sntq-daily_status');
this.waiting_ = $('sntq-waiting');
this.seated_ = $('sntq-seated');
this.oneFour_ = $('sntq-one-four');
this.fiveSix_ = $('sntq-five-six');
this.seven_ = $('sntq-seven');
this.running_ = true;
this.loadWaitList_();
this.intervalId_ = this.loadWaitList_.periodical(1200000, this);
window.addEventListener('focus', function() {
if (!this.running_) {
this.loadWaitList_();
this.intervalId_ = this.loadWaitList_.periodical(1200000, this);
}
}.bind(this));
window.addEventListener('blur', function() {
clearInterval(this.intervalId_);
this.running_ = false;
} .bind(this));
},
View
<div id="sntq-daily_status">
<div class="loading"></div>
</div>
I don´t think I completely understood your question, but to me it looks like you are using jQuery to get the containers at the top of your code.
If this is the case you are probably just missing the ID selector.
Try to change the code to
this.waitlist_ = $('#sntq-waitlist');
this.daily_status_ = $('#sntq-daily_status');
//[...]
(note the "#" selector if you want to look for elements by ID, which seems to be the case in your example).

How to achieve a queue of object instances of jQuery modals, assuring only one instance is on per time?

I've a task of building a modal prompt, that's been simple so far describing its methods like "show", "hide" when it comes down just to DOM manupulation.
Now comes the hardship for me... Imagine we have a page on which there are several immediate calls to construct and show several modals on one page
//on page load:
$("browser-deprecated-modal").modal();
$("change-your-city-modal").modal();
$("promotion-modal").modal();
By default my Modal (and other libraries i tried) construct all of these modals at once and show them overlapping each other in reverse order -
i.e $(promotion-modal) is on the top, while the
$("browser-deprecated-modal") will be below all of them. that's not what i want, let alone overlapping overlays.
I need each modal to show up only when the previous one (if there'are any) has been closed. So, first we should see "browser-deprecated-modal" (no other modals underneath), upon closing it there must pop up the second one and so on.
I've been trying to work it out with this:
$.fn.modal = function(options) {
return this.each(function() {
if (Modal.running) {
Modal.toInstantiateLater.push({this,options});
} else {
var md = new Modal(this, options);
}
});
}
destroy :function () {
....
if (Modal.toInstantiateLater.length)
new Modal (Modal.toInstantiateLater[0][0],Modal.toInstantiateLater[0][1]);
}
keeping a track of all calls to construct a Modal in a array and in the "destroy" method make a check of this array is not empty.
but it seems awkward and buggy me thinks.
i need a robust and clear solution. I've been thinking about $.Callbacks or $.Deferred,
kinda set up a Callback queue
if (Modal.running) { //if one Modal is already running
var cb = $.Callbacks();
cb.add(function(){
new Modal(this, options);
});
} else { //the road is clear
var md = new Modal(this, options);
}
and to trigger firing cb in the destroy method, but i'm new to this stuff and stuck and cannot progress, whether it's right or not, or other approach is more suitable.
Besides, I read that callbacks fire all the functions at once (if we had more than one extra modal in a queue), which is not right, because I need to fire Modal creation one by one and clear the Callback queue one by one.
Please help me in this mess.
My code jsfiddle
I got rid of the counter variable, as you can use toInstantiateLater to keep track of where you are, and only had to make a few changes. Give this a try...
Javscript
function Modal(el, opts){
this.el = $(el);
this.opts = opts;
this.overlay = $("<div class='overlay' id='overlay"+Modal.counter+"'></div>");
this.wrap = $("<div class='wrap' id='wrap"+Modal.counter+"'></div>");
this.replace = $("<div class='replace' id='replace"+Modal.counter+"'></div>");
this.close = $("<span class='close' id='close"+Modal.counter+"'></span>")
if (Modal.running) {
Modal.toInstantiateLater.push(this);
}
else {
Modal.running = true;
this.show();
}
}
Modal.destroyAll = function() {
Modal.prototype.destroyAll();
};
Modal.prototype = {
show: function() {
var s = this;
s.wrap.append(s.close);
s.el.before(s.replace).appendTo(s.wrap).show();
$('body').append(s.overlay).append(s.wrap);
s.bindEvents();
Modal.currentModal = s;
},
bindEvents: function() {
var s = this;
s.close.on("click.modal",function(e){
s.destroy.call(s,e);
});
},
destroy: function(e) {
var s = this;
s.replace.replaceWith(s.el.hide());
s.wrap.remove();
s.overlay.remove();
if (Modal.toInstantiateLater.length > 0) {
Modal.toInstantiateLater.shift().show();
}
else {
Modal.running = false;
}
},
destroyAll: function(e) {
Modal.toInstantiateLater = [];
Modal.currentModal.destroy();
}
}
Modal.running = false;
Modal.toInstantiateLater = [];
Modal.currentModal = {};
$.fn.modal = function(options) {
return this.each(function() {
var md = new Modal(this, options);
});
}
$("document").ready(function(){
$("#browser-deprecated-modal").modal();
$("#change-your-city-modal").modal();
$("#promotion-modal").modal();
$("#destroy-all").on("click", function() {
Modal.destroyAll();
});
});
jsfiddle example
http://jsfiddle.net/zz9ccbLn/4/

if statement within function breaks javascript

I'm stumped with this one and would really appreciate someone's help.
I'm customizing highslide for integration with wordpress. Via the following code within the highslide.config.js file I'm adding a class name to certain elements and passing different attributes through an onClick call depending on certain conditions.
Everything works until I add the following code:
if(hsGroupByWpGallery){
slideshowGroup: this.parentNode.parentNode.parentNode.id
};
When the above code is present, not only does that one statement not execute, but the whole thing stops working. Even if the if statement is something like if(1=1){}; it still breaks.
If I have instead simply slideshowGroup: this.parentNode.parentNode.parentNode.id or nothing (the two options I'm looking for), both do what I would expect. I just need an if statement to switch between them.
Here's the relevant code:
jQuery(document).ready(function() {
var hsCustomGalleryGroupClass = 'fbbHighslide_GalleryGroup';
var hsCustomGalleryGroupChecker = 0;
var hsGroupByWpGallery = true;
jQuery('.' + hsCustomGalleryGroupClass).each(function(){
hsCustomGalleryGroupChecker++;
return false;
});
if (hsCustomGalleryGroupChecker > 0){
jQuery('.' + hsCustomGalleryGroupClass).each(function(i, $item) {
var grpID = $item.id;
jQuery('#' + grpID + ' .gallery-item a').addClass('highslide').each(function() {
this.onclick = function() {
return hs.expand(this, {
slideshowGroup: grpID
});
};
});
});
} else {
jQuery('.gallery-item a').addClass('highslide').each(function() {
this.onclick = function() {
return hs.expand(this, {
// This is the problem if statement
if(hsGroupByWpGallery){
slideshowGroup: this.parentNode.parentNode.parentNode.id
};
});
};
});
};
});
Thanks in advance.
The problem is you are trying to assign a conditional property.. you can't have a if condition inside a object definition like that
jQuery('.gallery-item a').addClass('highslide').each(function () {
this.onclick = function () {
var obj = {};
//assign the property only if the condition is tru
if (hsGroupByWpGallery) {
obj.slideshowGroup = this.parentNode.parentNode.parentNode.id;
}
return hs.expand(this, obj);
};
});
Another way to do the same is
jQuery('.gallery-item a').addClass('highslide').each(function () {
this.onclick = function () {
//if the flag is true sent an object with the property else an empty object
return hs.expand(this, hsGroupByWpGallery ? {
slideshowGroup: this.parentNode.parentNode.parentNode.id
} : {});
};
});
I think you might want this, based on the other code:
jQuery('.gallery-item a').addClass('highslide').each(function() {
this.onclick = function() {
if(hsGroupByWpGallery){
return hs.expand(this, {
slideshowGroup: this.parentNode.parentNode.parentNode.id
});
}
};
});

Categories