Call a function outside of ViewModel scope - javascript

I have the following code outside the scope of my view model and i am attempting to call it with a click binding.
function Policy(data) {
var self = this;
Object.keys(data).forEach(function(prop) {
self[prop] = data[prop];
});
var generate = function() {
console.log("test");
// window.open("www.google.com")
// window.open("{{ url_for('genreport') }}/" + qvm.letter() + '/' + self.id);
}
}
however, when i try to call the function i get that generate is not defined. the code for my binding is below
<button class="btn btn-lg btn-primary btn-block" data-bind="click: function(){ generate() }">Generate</button>
I have tried calling Policy.generate, $data.generate, and i cannot get this function to call.
I know this issue is simple and im probably missing something that should be smashing me in the face but i'm oblivious, any help would be appreciated.

The fix was just to place the said function within the scope of my ViewModel, it is now fixed.

You have declared generate using a local variable. It is not visible outside the scope of Policy. If you want to create Policy.generate, you should do that:
Policy.generate = function () {

Related

Why is 'this' losing context in new Angular 1.5 components?

Got a very odd issue coming up here with the new components. When we had a 1.4 directive we had the following code...
(function () {
'use strict';
angular.module('app.board').directive('dcCb', dcClipboardCopy);
function dcCb() {
return {
link : function(scope, elem) {
var clipboard = new Clipboard(elem[0]);
elem.on('$destroy', function() {
clipboard.destroy();
});
}
};
}
})();
Inside the clipboard.destroy() function is the following...
Clipboard.prototype.destroy = function(){
this.listeners.destroy();
}
In 1.4 this is the same as the element so...
<button class="btn btn-sm btn-menu-outline copy-button" ...
So this worked fine as the button element seemed to have the listeners property which could be invoked.
However after the upgrade to 1.5 and now we have a component like this....
(function() {
'use strict';
angular.module('app.board').component('dcCb', {
...
controller: [ '$element','$scope',function($element,$scope) {
var self = this;
self.$postLink = postLink;
function postLink(){
var clipboard = new Clipboard($element[0]);
...
$element.on('$destroy', clipboard.destroy);
}
}]
});
})();
this (when inside the destroy function of the Clipboard) is now the controller object. So trying to call this.listeners throws an error.
First Question :
I understand that this in new components is the component scope but in 1.4 it was the button element. Surely in both the button element should be $element? Were we doing something wrong in 1.4?
Second Question :
Shouldn't var clipboard = new Clipboard($element[0]) force the context of this inside the clipboard to always be the clipboard itself (due to the new keyword)?
You're handing a function, which is arbitrarily defined on a class, off to the window and event listeners to be executed in a different context than the instance of Clipboard:
$element.on('$destroy', clipboard.destroy);
This is a fundamental concept of execution context in javascript, and I'd recommend reading up on it. But you can easily solve your current problem by simply binding the context of the function you are passing:
$element.on('$destroy', clipboard.destroy.bind(clipboard));

JS: ReferenceError: function is not defined

When my HTML file is loaded, it automatically shows a "login window" (see the function draw_login). When I click on the generated button I get the following error:
ReferenceError: emit_login is not defined
window.onload = function() {
var socket = io();
draw_login ();
function emit_login() {
var login_name = document.getElementById("login_name").value;
var login_password = document.getElementById("login_password").value;
socket.emit('login', {
name:"",
pw:""
});
}
function draw_login() {
document.getElementById("status_bar").innerHTML =
'Name:<input type="text" id="login_name"></input><br>'+
'Password:<input type="password" id="login_password"></input><br>'+
'<button type="button" onclick="emit_login();">login</button>';
}
}
Has anyone an idea or some suggestions?
Thanks in advance!
As Daniel A. White said;
Move it outside of the onload function.
And as Amit said, if you want to learn about why you need to do this, you should read about scopes in JavaScript as this is what causes your error. The function emit_login is created inside of the anonymous function window.onload, which means that anything outside of window.onload will not have access to anything outside of this.
Please correct me if I said anything wrong here. Haven't used JS for quite some time.
It looks like you are receiving this error because you have the emit_login function being called from your click handler which does not share the same scope as the function being called in your onload.
https://jsfiddle.net/sL7e5cut/
function emit_login() {
var login_name = document.getElementById("login_name").value;
var login_password = document.getElementById("login_password").value;
alert('login', {
name:"",
pw:""
});
}
(function() {
draw_login ();
function draw_login() {
document.body.innerHTML =
'Name:<input type="text" id="login_name"></input><br>'+
'Password:<input type="password" id="login_password"></input><br>'+
'<button type="button" onclick="emit_login();">login</button>';
}
}())
try defining the emit_login function outside the onload handler, and everything should work fine.
Keeping things out of the global scope is a good thing, but when you combine it with inline eventlisteners, e.g onclick="emit_login()" it just doesn't work.
Instead of having an inline eventlistener, you can do something like this in draw_login:
function draw_login() {
document.getElementById("status_bar").innerHTML =
'Name:<input type="text" id="login_name"></input><br>'+
'Password:<input type="password" id="login_password"></input><br>'+
'<button type="button">login</button>';
document
.querySelector('#status_bar button')
.addEventListener('click', emit_login, false);
}
Update: The whole point being that using the onclick attribute is discouraged, and definitely not one of the good parts of javascript.

Input's value as variable is not defined

Got an input type text. Whatever entered is supposed to become a value for variable, and further on from there. Yet, i get error "Uncaught ReferenceError: emailvar is not defined" and the whole script breaks from there.
html
<input type="text" class="signfield emfield" />
<div class="submt sbmtfrm" href="#" style="cursor:pointer;">Step 2</div>
and js
$(".sbmtfrm").click(function(){
var emailvar = $(".emfield").val();
});
In your code, emailvar is being defined in a function closure, and only that function has access to it.
$(".sbmtfrm").click(function(){
var emailvar = $(".emfield").val();
});
If you want to use emailvar outside of your jQuery event handler, you will need to first define it (not assign it, yet) outside the scope of the function.
(function(window, $) { // closure
var emailvar;
$(".sbmtfrm").click(function() {
emailvar = $(".emfield").val();
});
// you now have access to `emailvar` in any function in this closure
}(window, jQuery));
You need to declare emailvar as a global variable to use it outside of that click event handler:
$(function()
{
var emailvar;
$(".sbmtfrm").click(function()
{
emailvar = $(".emfield").val();
});
function foo()
{
console.log(emailvar);
}
}

Accessing function from within another with Javascript

I'm trying to get the jquery loadmask addon to work that will mask elements (for loading content). I'm using knockout.js, and when if I mask an element outside of my viewmodel it works, but I want to mask it upon submitting a POST request, and then unmask when I receive it. I'm getting an "object has no method mask" error from this. I'm not quite sure how to go about setting up an object to access it.
This works, but it's not what I want. I noted in the code where I would like to call mask from
<div id = "register_container">
<div data-bind="visible: register()">
<div id = "register_form"> <!--this is the div I want to mask -->>
<button data-bind="click: submitRegistration">Submit</button>
</div>
</div>
</div>
function MyViewModel(){
self.submitRegistration = function(){
//I want to mask here. When I try it says Object[object object] has no method mask
$.post....{
if(data.result == success){
// andunmask here
}
}
}
}
$("#register_form").mask("Waiting..."); //the masking works when I place it here, but it's always enabled and I want it inside the viewmodel where I noted so it only works when the POST request is in process
That's great and all, but I want to mask something from inside the viewmodel where I noted. How can I accomplish this?
I see several things that could be the problem.
Frist, you're doing assignment as opposed to comparison in the if statement. Use this instead:
if(data.result == success){
or even
if(data.result === success){
Second is the fact that I don't quite understand your code self.submitRegistration(){, which typically looks more like this:
var MyViewModel = function () {
var self = this;
self.submitRegistration = function() {
};
};
Then, if I mock the $.post call, it would work like this:
var MyViewModel = function () {
var self = this;
self.register = ko.observable(true);
self.submitRegistration = function() {
$("#register_form").mask("Waiting...");
// Mock $.post
window.setTimeout(function () {
if (1 == 1) {
// andunmask here
$("#register_form").unmask();
}
}, 3000);
}
};
ko.applyBindings(new MyViewModel());
See this fiddle for a demo.
You could even have Knockout help you find the element to look for:
See this updated fiddle for a demo of that.
// Use the "event" parameter to find the element...
self.submitRegistration = function(data, event) {
$(event.target).closest('#register_form').mask("Waiting...");
Hope it helps.

Calling a function from an html document will not generate an alert

Hi all thanks for taking a look.
I am trying to call a javascript function when I click on the update button.
Here is the javascript
var text2Array = function() {
// takes the value from the text area and loads it to the array variable.
alert("test");
}
and the html
<button id="update" onclick="text2Array()">Update</button>
if you would like to see all the code check out this jsfiddle
http://jsfiddle.net/runningman24/wAPNU/24/
I have tried to make the function global, no luck, I can get the alert to work from the html, but for some reason it won't call the function???
You have an error in the declaration of the pswdBld function in your JavaScript.
...
var pswdBld() = function() {
---^^---
...
This is causing a syntax error and avoiding the load of your JavaScript file.
See the corrected version.
Also, you may consider binding the event and not inlining it.
<button id="update">Update</button>
var on = function(e, types, fn) {
if (e.addEventListener) {
e.addEventListener(types, fn, false);
} else {
e.attachEvent('on' + types, fn);
}
};
on(document.getElementById("update"), "click", text2Array);​
See it live.
In your fiddle, in the drop-down in the top left, change "onLoad" to "no wrap (head)"
Then change
var text2Array = function()
var pswdBld() = function()
to
function text2Array()
function pswdBld()
and it will alert as expected.
You have a syntax error in the line below..
var pswdBld() = function
^--- Remove this
supposed to be
var pswdBld = function
Also make sure you are calling this script just at the end of the body tag..
Because you are using Function Expressions and not Function Declaration
var pwsdBld = function() // Function Expression
function pwsdBld() // Function Declaration
Check Fiddle

Categories