Recently I've separated out ViewModel to a separate JavaScript file.
var Report = (function($) {
var initialData = [];
var viewModel = {
reports: ko.observableArray(initialData),
preview: function(path) {
// preview report
},
otherFunctions: function() {}
};
return viewModel;
})(jQuery);
Here is the HTML and Knockout related code
<script type="text/javascript" src="path/to/report/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
ko.applyBindings(Report, document.body);
});
</script>
HTML user interface has a button on which click is data bind to preview function in the view model
<input type="button" name="Preview" id="Preview" class="btnPreview"
data-bind="click: Report.preview('url/to/report')" />
Problem preview method is called when the following line execute in $(document).ready() function
ko.applyBindings(Report, document.body);
That is without user clicking on the Preview button preview function is fired. What could be the reason for this behavior? The whole stuff was working fine when I'd view model JavaScript in the HTML page itself.
The reason is, that you're indeed invoking the preview function (because writing functionName means referring to the function, writing functionName() means calling it).
So data-bind="click: Report.preview" would be working as expected, but without handing over the parameter.
As the manual states (on a different topic, but this still applies):
If you need to pass more parameters, one way to do it is by wrapping your handler in a function literal that takes in a parameter, as in this example:
<button data-bind="click: function(data, event) { myFunction(data, event, 'param1', 'param2') }">
Click me
</button>
or in your case:
data-bind="click: function() { Report.preview('url/to/report') }"
Another solution would be to make preview() return a function (pretty much the same thing actually):
preview: function(path) {
return function() {
// ...
}
}
Another solution is to use 'bind' construct:
data-bind="click: Report.preview.bind($data, 'url/to/report')"
where the first parameter to bind() will become the 'this' in the called function.
Related
I know this is not quite possible or rather feasible. But , this is what I intend to do .
I have a JQuery Plugin named sumoselect (https://hemantnegi.github.io/jquery.sumoselect/) for multi select dropdown like feature which has a static link at the bottom.
addSumoSelect: function () {
$('#intGroup1').SumoSelect({ createNew: true, countList : true});
}
This createNew() is a function defined in sumoselect JQuery plugin file :
createNew: function () {
var O = this;
O.optDiv.append($('<ul><li><a class="ispicon ispicon_plus" href="#addInt" data-toggle="modal" data-target="#addSchedule" data-bind="click:$parent.addGroup" title="Create New"> Create New</a></li></ul>'));
}
But the problem is JQuery is not able to parse data-bind and parent syntaxes as I suppose they are native to Knockout JS.
So , the click event is not being fired.
What can I do to make it work ?
The addGroup function is defined in my Knockout JS file.
UPDATE
Should I try to do something like :
$("#intrusionGroup1").click(function (element) {
element.parentNode.addIntrusion();
});
One possible way of rendering an HTML content which is wrapped in a string is to use the html binding, but for that too, you will need to use another knockout binding (unfortunately). So, the easiest way of doing this that I can think of now is to use jQuery itself and register a click event for that list item. For that you will need to make sure to have unique selectors for the list items.
You'll have to create a custom binding that initializes the widget and injects the custom html. Because a binding's init method is executed during ko.applyBindings, it has the chance to inject child elements early enough for them to be data-bound.
A data-bind has an init method that is called once when ko.applyBindings happens, and an update method that is called whenever a linked observable changes.
It would look something like this:
ko.bindingHandlers.sumoselect = {
init: function(elem, valueAccessor) {
// Initialize the widget, something like:
var widget = $(element).sumoselect(/* options */);
// By now, the new HTML should be injected and data-bound
// after `init` returns
// I'm not sure if the widget has a dispose method, but
// it's important to know you'll have to take care of this
// yourself.
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
// Something like:
// widget.destroy()
});
}
};
An example that shows you what won't work:
ko.applyBindings({
onClick: function() { console.log("CLICK"); }
});
document.querySelector(".myDiv").innerHTML += "<button data-bind='click: onClick'>click me</div>";
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<p>Test button:</p>
<div class="myDiv"></div>
An example that shows you what will work:
ko.bindingHandlers.injectButton = {
init: function(element) {
element.innerHTML += "<button data-bind='click: onClick'>click me</div>";
}
}
ko.applyBindings({
onClick: function() { console.log("CLICK"); }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<p>Test button:</p>
<div class="myDiv" data-bind="injectButton"></div>
I'm having a problem with a jasmine test together with a knockout-template:
The html is similar to this:
<body>
<my-widget params="value: $data></my-widget>
</body>
the widget has a click-binding:
<div class="my-widget">
<a id="#clickme" data-bind="click: doSomething">Click me</a>
</div>
the widget-javascript is like this:
ko.components.register('my-widget', {
viewModel : function(params) {
this.doSomething = function() {
// doing something
};
},
template: {
require: 'text!../../templates/my-widget.html'
}
});
All of this works perfectly in production, but in Jasmine/Jquery, triggering a click on $('#clickme') does not execute the doSomething.
The following is an excerpt from my jasmine test (It's been greatly simplified but should contain the essentials):
beforeEach(function (done) {
require(['specHelpers', 'knockout'],
function (specHelpers, knockout) {
specHelpers.loadFixtureIntoPage("page.html", "myPage"); // template and id to use
expect($('#myPage')).toExist();
done();
});
});
it("WILL NOT TRIGGER MY CLICK", function (done) {
ko.applyBindings(myPage.pageViewModel, $('#myPage'))[0]);
setTimeout(function() {
$('#clickme').click();
// doSomething is not called :(
done();
}, 300);
});
When console.logging the #clickme element I can see that it is present.
It seems that the click binding in the widget does not get applied properly. However, when I run the test in bdd and it's over and failed - I can manually click this element and doSomething does get called.
What am I doing wrong? As I said, running the actual application works perfectly. It just seems that jasmine cannot handle the click bindings properly - I don't have this problem with the regular click events that are set in the document.ready
You really shouldn't be testing button clicks like that - you can be more certain if you just call the function doSomething() directly. Same way that you don't test any internals of JQuery yourself.
If you really really want to test events on a fixture, have you tried just
$("#clickme").trigger('click');
Also, double check that fixture is inserted into DOM in when you debug the test (say via browser)
I have a function that gets called during a control update where 'This' is passed so the function knows what control is updating. I am also calling the function at page load to get initial values but sending document.getElementById() doesn't seem to work. What should I be passing here?
For example:
<script type="text/javascript">
window.onload = function () {
alert("Got to window.onload");
materialSelectionChange(document.getElementById('SquaresDropDownList'));
};
</script>
in the js file...
function materialSelectionChange(obj) {
alert("Got into function");
}
Also, it does fire the js function on change properly
EDIT
The problem seems to be the load time of the JS document. The function wasn't successfully being called at that point because apparently the JS file hadn't finished loading. Likely because of window.onload. It works if I move the function into the page rather than in the JS file. Is there a way I can add delay so I know the page and it's components are fully loaded?
You are not delegating for window load event, you are invoking it, also your missing quotes around the id:
window.onload = myFunction(document.getElementById(typeSelect));
Try wrapping it around:
window.onload = function() {
myFunction(document.getElementById('typeSelect')); //id in quotes
};
EDIT
You must take care of js file import, import must be first before invoking the function within:
<script src='your-script-file.js'></script>
<script type="text/javascript">
window.onload = function () {
materialSelectionChange(document.getElementById('SquaresDropDownList'));
};
</script>
<select id="typeSelect" onchange="myFunction(this)">
window.onload = function(){
myFunction.bind(document.getElementById('typeSelect'));
}
The problem seems to be the load time of the JS document. The function wasn't successfully being called at that point because apparently the JS file hadn't finished loading. It works if I move the function into the page rather than in the JS file. Is there a way I can add delay so I know the page and it's components are fully loaded?
I'm trying to intercept clicks on this link:
<a id="test-button" href="#">Test</a>
with this js:
(function() {
$('#test-button').click(function (event) {
console.log("This code never gets called.")
event.preventDefault();
$('#alert-placeholder').html(['<div class="alert"><a class="close"',
'data-dismiss="alert">×</a>',
'<span>"+message+"</span></div>'].join())
return false;
})
console.log("yes, this code loads");
debugger;
})();
but the URL '#' loads and the code in the click() function doesn't run. What am I missing?
I'm using this code in a flask app using bootstrap.
Seems like you're trying to attach an event handler to the element that doesn't exist yet
(function() {
})();
only creates a local scope but doesn't guarantee DOM to load. To confirm it - add console.log($('#test-button').length); to your code.
What you need is to wrap your code with
$(function() {
// your code is here
});
instead
Here's my code
<script type="text/javascript">
function getNextScroll(i){
<asp:Literal ID="infiniteScrollTableJS" runat="server"></asp:Literal>
}
</script>
I am populating that Literal with some Javascript from the CodeBehind. This works great OnLoad. However, when I do a postback I have a problem. The Literal is updated on postback. The actual script that is inside the function actually gets changed (I can see it with Firebug). BUT, the function is unaware that there is new script in there, it just keeps running the old stuff.
So near as I can tell, I want to 're-initialize' the code inside the function after postback.
I know how to call a function after postback using add_endRequest.
This problem is not unique to .NET or anything. In fact, I have a simple example here -> http://jsfiddle.net/7JxY7/1/ 'Run Function' will always alert 'one' even after the script contents are changed.
I feel like jQuery probably has something that I could use, but searching around I have found nothing.
EDIT:
I tried the suggestion by mblase75. This works great in Javascript ( more importantly, now I understand why) , however, given this approach, I still can't get it working with the postback. Here is my updated code.
<script type="text/javascript">
function getNextScroll(i){
//some stuff
}
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function (s, e) {
//mblase75 suggestion
getNextScroll = function (i){<asp:Literal ID="infiniteScrollTableJS" runat="server"></asp:Literal>};
});
</script>
I thought this would work, but of course, it has the same problem as my original method. The Literal is actually changed, but the function doesn't know about the change, it still has the original code
In JavaScript, a function is actually an object. So instead of trying to rewrite the <script> element with a string, replace the function (variable) name with a new function object:
<script>
goTest = function(){
alert('one');
}
function changeTest1(){
goTest = function(){alert('two');};
alert('ok');
}
</script>
<a onclick="goTest();">Run Function</a><br/>
<a onclick="changeTest1();">Change Function</a>
http://jsfiddle.net/7JxY7/2/