AngularJs controller .appendChild() adding up each time view is visited - javascript

I have created a chat feature recently and I notice a curious bug. The following is the code I am using in my controller:
myFirebase.on('child_added', function(snapshot) {
var msg = snapshot.val();
var msgUsernameElement = document.createElement("b");
msgUsernameElement.textContent = msg.user;
var msgTextElement = document.createElement("p");
msgTextElement.textContent = msg.message;
var msgElement = document.createElement("div");
msgElement.appendChild(msgUsernameElement);
msgElement.appendChild(msgTextElement);
document.getElementById("messages").append(msgElement);
});
Suppose we enter the Chat view for the first time, then when I chat, the .appendChild() is working perfectly fine and the typed message shows up in the div 'messages'. Now Suppose we leave the Chat view and re-enter it, and type and send another chat message, then the .appendChild() is executed twice and the same message is appears twice in the div 'messages'. This continues linearly so if we re-enter the controller for the fifth time and send a message, the message is appended to the div 'messages' five times...
What is going on here?
I am using the Ionic Framework and the ready-made app template 'Tabs'. My chat feature is in the chat-details controller.

The problem is because the event handler is being added twice, once when you first visit the chat page, and again when you come back. To confirm that, visit it a third time, and you should see it 3x.
The solution is to put in an $onDestroy handler to remove the event handler. If the event handler can't be removed (ie it doesn't have an off method), then you should set a variable to tell you that it is initialised, and you check that on initialising the page (to prevent you doing it twice)

So Mikkel gave the right suggestion to initialize a variable and basically use that as a flag to make sure that my event handler is not called multiple times, given multiple entries to the Chat view, but only once. The following is my solution:
if (window.localStorage.getItem("notVisited") == null){ //This initilizes a variable in localstorage
window.localStorage.setItem("notVisited","true");
}
var startListening = function() {
myFirebase.on('child_added', function(snapshot) {
var msg = snapshot.val();
var msgUsernameElement = document.createElement("b");
msgUsernameElement.textContent = msg.user;
var msgTextElement = document.createElement("p");
msgTextElement.textContent = msg.message;
var msgElement = document.createElement("div");
msgElement.appendChild(msgUsernameElement);
msgElement.appendChild(msgTextElement);
document.getElementById("messages").append(msgElement);
});
window.localStorage.setItem("notVisited","false");
}
if (window.localStorage.getItem("notVisited") == "true") {
startListening();
}
Now after implementing this, I ran into a new problem which was that with this new code, each time I re-entered the view, the previous chat messages would not show up. So I added this:
if (window.localStorage.getItem("notVisited") == "false"){
$http({
url: "https://uniff-f4136.firebaseio.com/" + chatKey + ".json",
method: "GET"
}).then(function (response) {
for (var i in response.data){
var msgUsernameElement = document.createElement("b");
msgUsernameElement.textContent = response.data[i].user;
var msgTextElement = document.createElement("p");
msgTextElement.textContent = response.data[i].message;
var msgElement = document.createElement("div");
msgElement.appendChild(msgUsernameElement);
msgElement.appendChild(msgTextElement);
document.getElementById("messages").append(msgElement);
}
});
}
In the end, this made my chat work though I do not like the repetition of code. I hope others find this insightful.
Finally, to learn about using firebase to build a real time web chat app, visit this Google CodeLabs link: https://codelabs.developers.google.com/codelabs/cloud-firebase-chat/#4

Related

How can I deal with asynchronous requests involving modal popups in Casperjs?

Trying to iterate through a list of links that open modal popups, I'm running into an issue with the asynchronous nature of Javascript. I can loop through the links, and I can get Casperjs to click on all of the links. The popup opens up well (and I need to save the content of that popup). However, my code leads to Casperjs skipping every few links -- I suspect that's because of the delay. I need to be sure that every link is clicked and every popup saved. Any hint is highly appreciated!
I'm aware of Casperjs wait and waitForSelector functions, but no matter where I put them -- it still skips some popups. I suppose the reason for this behaviour is the delay, but increasing/decreasing the wait values and places where I tell casperjs to wait don't help.
this.then(function(){
x = 0;
this.each(links,function(self,link){
// I only need links that contain a certain string
if(link.indexOf('jugyoKmkName')>=0) {
var coursetitle = linktexts[x];
this.clickLabel(linktexts[x], 'a');
this.wait(2000, function() {
var coursetitleSplit = coursetitle.split(' ');
var courseid = coursetitleSplit[0];
//this logs the title and id in a file. Works perfectly
var line = courseid+' '+coursetitle+' \\n';
fs.write('/myappdirectory/alldata.txt', line, 'a');
//this logs the popup contents -- but it's completely out of sync
var courseinfo = this.getElementInfo('.rx-dialog-large').html
fs.write('/myappdirectory/'+courseid+'.html', courseinfo, 'w');
});
}
x++;
});
});
I'm logging two things here -- the link text (and some more information) in a running log file. That's working fine -- it catches every link correctly. The link text contains a unique id, which I'm using as a file name to save the popup contents. That's only working on every nth popup -- and the popup contents and the id are out of sync.
To be precise: The first 10 ids in the list are:
20000 -- saved with this id, but contains data of popup 20215
20160 -- saved with this id, but contains data of popup 20307
20211 -- saved with this id, but contains data of popup 20312
20214 ...etc (saved, but with popup from an ID way further down the list)
20215
20225
20235
20236
20307
20308
Obviously, I need the file 2000.html to save the contents of the popup with the ID 20000, 20160 with the contents of 20160 etc.
Presumably this.each(links,...) will run the callback synchronously rather than waiting for each this.wait() call to complete. Instead you'll want to wait until you've written your data to the filesystem before processing the next link. Consider this code instead:
this.then(function() {
function processNthLink(i) {
var self = this;
var link = links[i];
if (link.indexOf('jugyoKmkName')>=0) {
var coursetitle = linktexts[i];
self.clickLabel(linktexts[i], 'a');
self.wait(2000, function() {
var coursetitleSplit = coursetitle.split(' ');
var courseid = coursetitleSplit[0];
var line = courseid+' '+coursetitle+' \\n';
fs.write('/myappdirectory/alldata.txt', line, 'a');
var courseinfo = self.getElementInfo('.rx-dialog-large').html
fs.write('/myappdirectory/'+courseid+'.html', courseinfo, 'w');
if (i < links.length) {
processNthLink(i+1);
}
});
} else if (i < links.length) {
processNthLink(i+1);
}
}
processNthLink(0);
});
In this case the the next link will only be processed after the timeout and write to FS has been completed. In the case that the link doesn't contain the expected string, the next link is processed immediately.

Retrieving the error elements in Pega 8.1 Tree Navigation Flow

I am currently working on Pega. I am basically new in it. Pega version which I am working is 8.1.
Actually in the flow while submitting a form whenever there are validation errors in the required fields there an alert pops up. But as far as UI and UX the window needs to scroll with a subtle animation to the error fields, this I can do perfectly for any HTML page using jquery, but as I am new to Pega, I am facing a problem to get the error fields.
What I find, the error fields are appending on the relevant fields whenever there are validations, but catching those elements are going undefined.
I am sharing the simple code which I have written:
var validateScroll = function(){
var getErrors = function(){
this.parent = "";
}
getErrors.prototype.getErrorfn = function(node){
this.parent = $(node);
var errorLabels = this.parent.find('.dynamic-icon-error');
console.log(errorLabels.attr('class'))
}
var a = new getErrors();
var scrollfn = function(){
var forms = $(document).find('form')[0];
var _FORM = $(forms);
_FORM.on('submit',a.getErrorfn(_FORM));
}
return{
init:function(){
scrollfn();
}
}
}();
$(document).ready(function(){
validateScroll.init();
})
The result I am getting is "undefined", since those are appending and not caught in the DOM.
Can anyone please suggest me any process? Is there any API to get the error fields in javascript. I have searched their Javascript API, but couldn't find any such API.
I have also asked this on PDN but got no reply.
Thanks in advance.

How can I successfully add a new button to the top bar of Reddit new layout

I'm trying to create a user script to add a new button to the new Reddit design so I can return to the old layout easily.
The new button goes somewhere around here.
This is my simple code:
var parent = document.getElementsByClassName('s17th0jh-5 iAfVlC')
var bOld = document.createElement("button");
bOld.innerHTML = "old";
parent[0].appendChild(bOld);
bOld.addEventListener("click", function() {
var url = document.URL;
url = url.replace('/www.', '/old.');
window.location.href = url;
});
The problem is, every time I load or reload the page the new button appears and quickly disappears within a second. How can I fix this?
Can you add your function in a window.load function or something? It sounds like reddit has some javascript which is executing after yours (which has a side effect of deleting your button). If you can get yours to execute after reddit's perhaps it would work?
window.addEventListener('load', function() { /*YOUR CODE*/ });
The problem might be because since reddit is using react , your changes are getting overwritten due to re - rendering. Because after your script executes the component gets re - rendered.
But the solution to this problem is using setTimeout .In this, after the component/header is done rendering , your script will then append your button/link
var parent = document.getElementsByClassName('s17th0jh-5 iAfVlC')
var bOld = document.createElement("button");
bOld.innerHTML = "old";
setTimeout(function(){
parent[0].appendChild(bOld);
bOld.addEventListener("click", function() {
var url = document.URL;
url = url.replace('/www.', '/old.');
window.location.href = url;
});
},1000)

logging me out?

var account;
var users = ["minibluerhino"];
var password = "killer224";
var gameId = 126945035 //Put your game ID here
for (account = 0; account < users.length; account++) {
window.location.assign('www.roblox.com');
$(document).ready(function(){
document.getElementById("LoginUsername").value = users[account];
document.getElementById("LoginPassword").value = password;
document.getElementById('LoginButton').click();
$(document).ready(function(){
window.location.assign('http://www.roblox.com/---place?id=' + gameId);
});
});
};
Whenever I run that code, it brings me to the target location, but I am notlogged in, If I remove the target location, it logs me in perfectly fine.
Firstly if i am not mistaken, this should be written more like
var account;
var users = ["minibluerhino"];
var password = "killer224";
var gameId = 126945035 //Put your game ID here
$(document).ready(function(){
for (account = 0; account < users.length; account++) {
window.location.assign('www.roblox.com');
document.getElementById("LoginUsername").value = users[account];
document.getElementById("LoginPassword").value = password;
document.getElementById('LoginButton').click();
window.location.assign('http://www.roblox.com/---place?id=' + gameId);
};
});
Or This?
var account;
var users = ["minibluerhino"];
var password = "killer224";
var gameId = 126945035 //Put your game ID here
$(document).ready(function(){
for (account = 0; account < users.length; account++) {
window.location.assign('www.roblox.com');
document.getElementById("LoginUsername").value = users[account];
document.getElementById("LoginPassword").value = password;
document.getElementById('LoginButton').click();
};
window.location.assign('http://www.roblox.com/---place?id=' + gameId);
});
Its hard to know what logic your going for exactly?
But the ready function simply means fun this code when the DOM is ready, as in everything on the page has loaded.
The window.location would be called for each loop which does not make sense?
EDIT:
I think i understand your problem now more so, Your trying to run this as a script against a web page to auto login you.. Say something like a grease monkey/user script..
The problem here is, Your entering the username and password into the boxes and click "login" as such.. But straight afterwards you are redirecting the browser before it even has time to process the login.
Going to guess a successful login on this site does not auto-reload the page, so hence your attempting this or trying to over ride the default page it loads as your home page.
In this case, you want to either use a setTimeout or handle another javascript on the page that they do load.
If their login does not reload, but simply changes some elements.. Listen to their events and trigger your reload when that happens.
SECOND EDIT
Try the following, click did not work on the button.. but because it is a form you can submit it using the form element.
var account;
var users = ["minibluerhino"];
var password = "killer224";
var gameId = 126945035 //Put your game ID here
$(document).ready(function(){
for (account = 0; account < users.length; account++) {
window.location.assign('www.roblox.com');
document.getElementById("LoginUsername").value = users[account];
document.getElementById("LoginPassword").value = password;
document.getElementById('LogInForm').submit();
};
// window.location.assign('http://www.roblox.com/---place?id=' + gameId);
});
Being this is a bot, without knowing the further details. This might go against the stackoverflow terms and conditions or something. So going to have to limit what more support i can give.
In general i would suggest if your running scripts to help you login to websites or customize and automate them for you. Have a look at user scripts and you will see they have a world of use.
Have one that gives you a custom GUI or additional elements on a website when your logged in or just simple a script that auto logins you to any site in its list and so on.

Accessing contents of NativeWindow in a HTML AIR application?

I'm currently building a HTML/JS AIR application. The application needs to display to the user a different 'window' - dependant on whether this is the first time they've launched the application or not. This part is actually fine and I have the code below to do that:
if(!startUp()) { // this simply returns a boolean from a local preferences database that gets shipped with the .air
// do first time stuff
var windowOptions = new air.NativeWindowInitOptions();
windowOptions.systemChrome = 'none';
windowOptions.type = 'lightweight';
windowOptions.transparent = 'true';
windowOptions.resizable = 'false';
var windowBounds = new air.Rectangle(300, 300, 596, 490);
var newHtmlLoader = air.HTMLLoader.createRootWindow(true, windowOptions, true, windowBounds);
newHtmlLoader.load(new air.URLRequest('cover.html'));
}
else {
// display default window
// just set nativeWindow.visible = true (loaded from application.xml)
}
However, what I want to be able to do is manipulate the html content from within cover.html after it has loaded up. There seems to be plenty of tutorials online of how to move, resize, etc. the NativeWindow, but I simply want access to the NativeWindow's HTML content.
For example, how would I add a new paragraph to that page? I've tried the following:
newHtmlLoader.window.opener = window;
var doc = newHtmlLoader.window.opener.document.documentElement;
Using AIR's Introspector console, ....log(doc) returns [object HTMLHtmlElement].
Hmm, seems promising right? I then go on to try:
var p = document.createElement('p');
var t = document.createTextNode('Insert Me');
p.appendChild(t);
doc.appendChild(p);
...but nothing gets inserted. I've also tried the following replacements for doc:
var doc = newHtmlLoader.window.opener.document.body; // .log(doc) -> [object HTMLBodyElement]
var doc = newHtmlLoader.window.opener.document; // .log(doc) -> Error: HIERARCHY_REQUEST_ERR: DOM Exception 3
...as well as the following with jQuery:
$(doc).append('<p>Insert Me</p>'); // again, nothing
So, anyone had any experience in accessing a NativeWindow's inner content programmatically? Any help will be greatly appreciated.
Hmm, so I think I may have found out how to do it...If we amend the original code and add an event listener on the loader:
var newHtmlLoader = air.HTMLLoader.createRootWindow(true, windowOptions, true, windowBounds);
newHtmlLoader.addEventListener(air.Event.COMPLETE, doEventComplete);
newHtmlLoader.load(new air.URLRequest('cover.html'));
You can then interact (assuming you're using jQuery) with the contents of the newly created window by using:
function doEventComplete(event) {
doc = $(event.currentTarget.window.document.body);
doc.append('<p>Insert Me!</p>')
}
:)
I'm not sure this has the effect you intended:
newHtmlLoader.window.opener = window;
var doc = newHtmlLoader.window.opener.document.documentElement;
What is does is set var doc = window.document.documentElement;, so 'doc' is your local document, not the one in the other window.
I think what you want is
var doc = newHtmlLoader.window.document.documentElement;
Do note that this will not work until the document has loaded.

Categories

Resources