JavaScript runtime errors about undefined or null refences - javascript

I have implemented a SettingsFlyout. From the view of this flyout, my app collection some info from user (firsname) and want to store it in roaming settings. This information get stored when user clicks a button the settings view and retrieved when in the beforeShow event for the flyout. These two events are setup in the ready function of the SettingsFlyout itself but for some reason I am getting following error.
0x800a138f - JavaScript runtime error: Unable to get property 'winControl' of undefined or null reference
on following line
var divtest = document.getElementById"test").winControl;
Similarly I also get
0x800a138f - JavaScript runtime error: Unable to set property 'onclick' of undefined or null reference.
Do you see anything I am doing wrong causing these issues?
Here is what I have in default.html
app.onsettings = function (e) {
e.detail.applicationcommands = {
"test": {
href: "/pages/settings/test/test.html",
title: "Test"
}
}
WinJS.UI.SettingsFlyout.populateSettings(e);
};
Here is the test.html itself.
<!DOCTYPE html>
<html>
<head>
<title></title>
<link href="/pages/settings/test/test.css" rel="stylesheet" />
<script src="/pages/settings/test/test.js"></script>
</head>
<body>
<div
data-win-control="WinJS.UI.SettingsFlyout" data-win-options="{settingsCommandId:'test', width:'narrow'}">
<div class="win-header">
<div class="win-label">test</div>
</div>
<div class="win-content">
First Name: <input id="firstname" />
<br />
<input type="submit" value="Save" />
</div>
</div>
</body>
</html>
Here is the test.js file.
(function () {
"use strict";
WinJS.UI.Pages.define("/pages/settings/test/test.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
// TODO: Initialize the page here.
var roamingSettings = Windows.Storage.ApplicationData.current.roamingSettings;
var divtest = document.getElementById("test").winControl;
var firstname = document.getElementById("firstname");
document.getElementById("submit").onclick = function (e) {
//alert('hi');
roamingSettings.values["firstname"] = firstname.value;
}
divtest.addEventListener("beforeshow", function () {
firstname.value = roamingSettings.values["firstname"];
});
},
unload: function () {
// TODO: Respond to navigations away from this page.
},
updateLayout: function (element, viewState, lastViewState) {
/// <param name="element" domElement="true" />
// TODO: Respond to changes in viewState.
}
});
})();

There are no elements that have the id submit or test in your HTML.

The problem may be because this:
var divtest = document.getElementById("test").winControl;
looks for the HTML element with Id=test, it seems that you set
settingsCommandId:'test'
but it's not the same, so is should be:
<div id="test" data-win-control="WinJS.UI.SettingsFlyout" data-win-options="{settingsCommandId:'test', width:'narrow'}">

Related

Why can't I access javascript function from HTML file?

Fairly basic question here. I've run into a situation where I can't seem to access Javascript functions from within my HTML file, even though I've linked the JS file as a script src. It seems like a pretty simple issue but I can't figure out what the problem is.
I'm trying to add a function called startLogin to an HTML button. I added it as an onclick, but then when I try to click the button, the console says the function is undefined. However the function is clearly defined in the JS file and as far as I can tell the syntax I'm using for the onclick and the script src link is correct.
In addition I've confirmed that the JS file is linked to the HTML file. If I try to manipulate the DOM from the JS file just to do something simple, like set the background to red, that works fine. The problem is when I try to call a function defined in the JS file. Also I've made sure the function I'm trying to call does actually work. If I stick it right in the HTML file inside script tags, it works fine.
I've already tried moving the script tags inside the body at the end of the HTML, as I know that's often the issue, but in this case it didn't work. Can anyone help me identify why I'm unable to access the "startLogin" function from the HTML button?
FYI, this is a javascript project and I'm using Vite.js for bundling. All the other HTML/JS files in my project are playing nicely together, I'm only having an issue with the Login page.
file structure:
|-pages
|-login.html
|-login.js
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-
scale=1.0" />
<title>Document</title>
<!-- LINK JS FILE -->
<!-- MODULE TYPE IS RELATED TO VITE.JS -->
<script type="module" src="./login.js"></script>
</head>
<body>
<!-- email login form -->
<form name="emailLogin" id="emailLogin" style="display: none">
<div class="row" style="width: 600px">
<div class="col">
<div class="form-row" style="padding-bottom: 10px">
<input
type="email"
class="form-control"
id="emailAddress"
placeholder="email associated with your login"
style="width: 576px"
/>
</div>
</div>
<div class="form-row">
<br />
<button type="button" class="btn btn-primary" onclick="startLogin('email')">
Send Email Login
</button>
</div>
</div>
</form>
</body>
</html>
login.js
// start the login process by generating a code sent either SMS or EMAIL
function startLogin(login_type) {
// local variables
var ajaxResult;
var subs;
var tempString;
// get the login values and set up the call
if (login_type == "phone") {
// get the values
use_country_code = $("#country").val();
use_phone = $("#phoneNumber").val();
use_phone = use_phone.replace(/\D/g, "");
// do the validation
if (use_phone.length < 10) {
$("#errorText").html(
"Phone number doesn't have enough digits, please try again."
);
$("#errorModal").modal("show");
return;
}
// build the url
post_url =
"https://us-central1-dev-api-327415.cloudfunctions.net/user-login?cc=" +
use_country_code +
"&phone=" +
use_phone;
} else {
// get the values
use_email = $("#emailAddress").val();
// do the validation
if (!validateEmail(use_email)) {
$("#errorText").html(
"Email address does not appear to be valid, please check the format and try again."
);
$("#errorModal").modal("show");
return;
}
// build the url
post_url =
"https://us-central1-dev-api-327415.cloudfunctions.net/user-login?email=" +
use_email;
}
// send the request to the server and process the results
$.LoadingOverlay("show");
$.ajax({
type: "POST",
url: post_url,
// process the returned result of the Ajax call
success: function (ajaxResult) {
// see if we have a session token and handle the response
session_token = ajaxResult["session_token"];
if (session_token == "None") {
// hide the login and show the text message area if phone, otherwise hide email and show email message
if (login_type == "phone") {
$("#loginMethod").hide();
$("#phoneLogin").hide();
$("#codeLogin").show();
$("#loginMessage").hide();
$("#textMessage").show();
} else {
$("#loginMethod").hide();
$("#emailLogin").hide();
$("#loginMessage").hide();
$("#codeLogin").show();
$("#emailMessage").show();
}
} else {
// hide everything since already logged in and show the right message
$("#phoneLogin").hide();
$("#emailLogin").hide();
$("#loginMethod").hide();
$("#loginMessage").hide();
$("#codeLogin").hide();
$("#continueLoginAlready").show();
}
},
// process after the Ajax call has been fully completed
complete: function () {
$.LoadingOverlay("hide");
},
// handle total failure
error: function (jqXHR, exception) {
console.log(jqXHR);
console.log(exception);
json_error = jqXHR["responseJSON"];
$("#errorText").html(json_error.error_message);
$("#errorModal").modal("show");
},
});
}
Javascript modules work a bit differently. There, variables and functions are not exposed to the global scope.
If you want to use your function from other parts of the code, you have to set it explicitly on the window object:
function startLogin(...) {
...
}
window.startLogin = startLogin;
an other solution is to set the js at end of the html, than you don't need to use the window object (memory lag)
<html lang="en">
<head>...</head>
<body>
<button type="button" id="myButton">Title</button>
</body>
<script>
function myFunction(){
console.log('running myFunction');
}
const button = document.querySelector('#myButton');
button.addEventListener('click', function clickListener(
{
myFunction();
}
</script>
</html>
the browser is simply stupid, it loads the page from top to bottom and if you load your js after the body all your html is present and you can do it this way.

JQuery not working through Google's CDN

For a few hours I've been trying to understand what's wrong. My purpose is to enable a button after textfields are filled. Code seems fine according to my test at JSFiddle but it's still not working on my server. Am'I missing something or is this a server problem (which is hard to believe since javascript is client-side)?
PS: I'm not expert at HTML, so I don't know how to identate it's syntax; if it's not that readable I'm sorry and would appreciate an edit-help. thanks.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="css/style.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript">
var $input = $('input:text'),
$apply = $('#apply');
$apply.attr('disabled', true);
$input.keyup(function() {
var trigger = false;
$input.each(function() {
if (!$(this).val()) {
trigger = true;
}
});
trigger ? $apply.attr('disabled', true) : $apply.removeAttr('disabled');
});
</script>
</head>
<body>
<section class="container">
<div class="OpenKore">
<div id="absolute">
<form method="GET" action="generate.php">
<fieldset>
<legend><h1>OpenKore Automatic Config:</h1></legend>
LOGIN:
<p><input type="text" id="id_login" name="login_value" value="" placeholder="Login"></p>
SENHA:
<p><input type="text" id= "id_senha" name="senha_value" value="" placeholder="Senha"></p>
PIN:
<p><input type="text" id="id_pin" name="pin_value" value="" placeholder="PIN"></p>
<input id="apply" type="submit" name="commit" disabled value="Gerar Configurações">
</fieldset>
</form>
</div>
</div>
</section>
</body>
</html>
When the browsers reads your HTML page, it reads top to bottom. When it gets to your <script> tags it runs them. Now it us doing this before it has got to the rest of the page, i.e. before it even knows about any body or form or input:text tags, so even though you code will run, it will simply not do anything because none of the elements on the page exist yet.
JavaScript 101, make the code run after the page has loaded, if you need to access elements on the page. How do you do that? either put the code at the bottom of the page (move your <script> tags to just before the </body> tag), or wrap your code in a function that is executed after the browser has finished loading the page. Now jQuery has a very helpful way of doing this for you, pass a function to jQuery and it will be executed after the page is loaded.
jsFiddle does this automatically for you, hence the drop down in the top left corner saying 'onLoad'
i.e. your code
$(); //this is the jQuery function
//This is your code wrapped in a function called 'yourCode'
function yourCode() {
var $input = $('input:text'),
$apply = $('#apply');
$apply.attr('disabled', true);
$input.keyup(function () {
var trigger = false;
$input.each(function () {
if (!$(this).val()) {
trigger = true;
}
});
trigger ? $apply.attr('disabled', true) : $apply.removeAttr('disabled');
});
}
$(yourCode); //this is passing the jQuery function a function,
//this will now be execute once the page is loaded
//or what most people do, pass in as an anonymous function
//which eliminates a step
$(function () {
var $input = $('input:text'),
$apply = $('#apply');
$apply.attr('disabled', true);
$input.keyup(function () {
var trigger = false;
$input.each(function () {
if (!$(this).val()) {
trigger = true;
}
});
trigger ? $apply.attr('disabled', true) : $apply.removeAttr('disabled');
});
});
as suggested by #j08691 I would suggest reading about the document ready in jQuery here

Definition of Function SubmitSurvey have not been found but I defined it

It is literally fifth day I try to solve this.
I try to invoke a method by a button in Razor View, no redirections to other views, just invoke a simple method when button is clicked.
The script looks like:
<script>
function SubmitClick () {
var pid = $(this).data('personid');
var sid = $(this).data('surveyid');
var url = '#Url.Action("SubmitSurvey", "Person")';
$.post(url, { personid: pid, surveyid: sid }, function (data) {
alert('updated');
});
};
</script>
The button looks like:
<button class='mybutton' type='button' data-personid="#Model.Item1.Id" data-surveyid="#survey.Id" onclick="javascript:SubmitClick()">Click Me</button>
The PersonController method looks like:
public void SubmitSurvey(int personId, int surveyId) {
System.Diagnostics.Debug.WriteLine("UPDATING DATABASE");
}
The full view (this is PartialView):
<script>
function SubmitClick () {
var pid = $(this).data('personid');
var sid = $(this).data('surveyid');
var url = '#Url.Action("SubmitSurvey", "Person")';
$.post(url, { personid: pid, surveyid: sid }, function (data) {
alert('updated');
});
};
</script>
#using WebApplication2.Models
#model System.Tuple<Person, List<Survey>>
<hr />
<h1>Surveys</h1>
<input type="button" id="Coll" value="Collapse" onclick="javascript:CollapseDiv()" />
#*<p>
Number of Surveys: #Html.DisplayFor(x => Model.Item2.Count)
</p>*#
#{int i = 1;}
#foreach (var survey in Model.Item2) {
using (Html.BeginForm()) {
<h2>Survey #(i)</h2>
<p />
#Html.EditorFor(x => survey.Questions)
<button class='mybutton' type='button' data-personid="#Model.Item1.Id" data-surveyid="#survey.Id" onclick="javascript:SubmitClick()">Click Me</button>
}
i++;
<hr style="background-color:rgb(126, 126, 126);height: 5px" />
}
<hr />
The problem is that when I click the button:
I get runtime error saying that there is no definition of: "SubmitClick".
I don't see any obvious problems in your code, but given that you're handling this in a sub-optimal way, refactoring your code may solve the problem just by improving the setup.
First, don't embed your scripts directly in the view. I understand that you need to include a URL generated via one of the Razor helpers, but what I'm talking about here is using sections so that your scripts get included in a standard location in the document:
So, in your view:
#section Scripts
{
<script>
// your code here
</script>
}
And then in your layout:
<!-- global scripts like jQuery here -->
#RenderSection("Scripts", required: false)
</body>
This ensures that 1) all your JavaScript goes where it should, right before the closing body tag and 2) all your JavaScript gets run after the various global scripts that it will likely depend on (jQuery).
Second, it's usually a bad idea to define things in the global scope, such as you are doing with your SubmitClick function. If another script comes along and defines it's own SubmitClick function in the global scope, then yours gets hosed or vice versa. Instead, you want to use namespaces or closures.
Namespace
var MyNamespace = MyNamespace || {};
MyNamespace.SubmitClick = function () {
...
}
Closure
(function () {
// your code here
})();
Of course, if you use a closure like this, then you SubmitClick function truly won't exist, as it's no longer in the global scope, which brings me to...
Third, don't use the on* HTML attributes. It's far better to bind functionality to elements dynamically, for example:
(function () {
$('.mybutton').on('click', function () {
var pid = $(this).data('personid');
var sid = $(this).data('surveyid');
var url = '#Url.Action("SubmitSurvey", "Person")';
$.post(url, { personid: pid, surveyid: sid }, function (data) {
alert('updated');
});
});
})();
Now, you've got zero scope pollution and behavior is bound where behavior is defined, instead of tightly-coupling your HTML and JavaScript.

AngularJS error: fnPtr is not a function

I'm trying to write a sample AngularJS, and SpringMVC project. The spring methods works fine, but I have a problem with declaraton of function in my site controller. My app should return a word from text input, but when I click the button, I've got this error:
[13:23:58.900] "Error: fnPtr is not a function
parser/_functionCall/<#http://localhost:8080/example/resources/js/Angular/angular.js:6542
ngEventDirectives[directiveName]</</</<#http://localhost:8080/example/resources/js/Angular/angular.js:13256
Scope.prototype.$eval#http://localhost:8080/example/resources/js/Angular/angular.js:8218
Scope.prototype.$apply#http://localhost:8080/example/resources/js/Angular/angular.js:8298
ngEventDirectives[directiveName]</</<#http://localhost:8080/example/resources/js/Angular/angular.js:13255
createEventHandler/eventHandler/<#http://localhost:8080/example/resources/js/Angular/angular.js:2095
forEach#http://localhost:8080/example/resources/js/Angular/angular.js:130
createEventHandler/eventHandler#http://localhost:8080/example/resources/js/Angular/angular.js:2094
"
This is my index.html:
<!DOCTYPE html>
<html lang="en" ng-app="Apken">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="resources/js/Angular/angular.js"></script>
<script src="resources/js/controler.js"></script>
</head>
<body ng-controller="theNamer">
<div class="input-append">
<input style="width:358px;" class="span2" type="text" ng-model="myName" required min="1" />
<button class="btn btn-primary" ng-disabled="!myName" ng-click="send()">Click!</button>
</div>
<ul>
<li ng-repeat="name in names">{{name}}</li>
</ul>
</body>
</html>
And controler.js:
function theNamer ($scope,$http){
$scope.myName='aa';
$scope.fetchList=new function()
{
$http.get('ca/list.json').success(function(thList){
$scope.names = thList;
});
}
$scope.send=new function()
{
$http.post('ca/set/3').success(function(){
$scope.fetchList;
});
}
$scope.fetchList;
}
var Apken = angular.module('Apken',[]);
Apken.controller('theNamer', theNamer);
I've noticed, that must be a some kind of problem with function declaration in the ng-click value. On site startup controler.js works fine, but it crashes, when I click the button.
Just wanted to add for anybody receiving this error, it can also be seen if you, like me, make the n00b mistake of creating a variable with the same name as function (the function being called from ng-click:
$scope.addTask = {};
$scope.addTask = function() {};
I have tested your code. Using AngularJS 1.0.7, the error disappears when you replace
$scope.send = new function() {
with
$scope.send = function () {
and same applies to fetchList.
I guess you mixed the two syntaxes function(*args*) { *body* } and new Function(*args*, *body*). Check on MDN: Function.
You have also to change your code in order to get your fetchList properly called:
function theNamer($scope, $http) {
$scope.myName = 'aa';
$scope.fetchList = function() {
$http.get('ca/list.json').success(function(thList) {
$scope.names = thList;
});
};
$scope.send = function() {
$http.post('ca/set/3').success(function() {
$scope.fetchList();
});
};
$scope.fetchList();
}

Getting "Cannot read property 'nodeType' of null" when calling ko.applyBindings

I have this knockout code:
function Task(data) {
this.title = ko.observable(data.title);
this.isDone = ko.observable(data.isDone);
}
function TaskListViewModel() {
// Data
var self = this;
self.tasks = ko.observableArray([]);
self.newTaskText = ko.observable();
self.incompleteTasks = ko.computed(function() {
return ko.utils.arrayFilter(self.tasks(), function(task) { return !task.isDone() });
});
// Operations
self.addTask = function() {
self.tasks.push(new Task({ title: this.newTaskText() }));
self.newTaskText("");
};
self.removeTask = function(task) { self.tasks.remove(task) };
}
ko.applyBindings(new TaskListViewModel());
This html:
<head>
<script type="text/javascript" src="jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="knockout-2.0.0.js"></script>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<h3>Tasks</h3>
<form data-bind="submit: addTask">
Add task: <input data-bind="value: newTaskText" placeholder="What needs to be done?" />
<button type="submit">Add</button>
</form>
<ul data-bind="foreach: tasks, visible: tasks().length > 0">
<li>
<input type="checkbox" data-bind="checked: isDone" />
<input data-bind="value: title, disable: isDone" />
Delete
</li>
</ul>
You have <b data-bind="text: incompleteTasks().length"> </b> incomplete task(s)
<span data-bind="visible: incompleteTasks().length == 0"> - it's beer time!</span>
</body>
The example is the same as the one found on the Knockout website, but when I run it, it returns this message on Chrome Fire Bug:
Uncaught TypeError: Cannot read property 'nodeType' of null
This one is related to the knockout file and to this line of my script:
ko.applyBindings(new TaskListViewModel());
And this error is pointing to this line (1766) on knockout:
var isElement = (nodeVerified.nodeType == 1);
What am I doing wrong?
This problem was happening because I was trying to bind an HTML element before it was created.
My script was loaded on top of the HTML (in the head) but it needed to be loaded at the bottom of my HTML code (just before the closing body tag).
Thanks for your attention James Allardice.
A possible workaround is using defer="defer"
<script src="script.js" type="text/javascript" defer="defer"></script>
Use this if the script is not going to generate any document content. This will tell the browser that it can wait for the content to be loaded before loading the script.
Further reading.
Hope it helps.
You might want to consider using the jquery ready handler for this
$(function() {
function TaskListViewModel() {
...
ko.applyBindings(new TaskListViewModel());
});
Then you achieve two things:
Avoid polluting the global namespace
Knockout binding occurs AFTER the DOM is created. You can place your javascript wherever it is suited for organization.
See http://api.jquery.com/ready/
if you have jQuery put apply binding inside onload so that knockout looks for the DOM when DOM is ready.
$(document).ready(function(){
ko.applyBindings(new TaskListViewModel());
});
You have a simple spelling mistake:
self.addTask = fuction() {
Should be:
self.addTask = function() { //Notice the added 'n' in 'function'

Categories

Resources