Knockout Click event not working - javascript

I am working on a weather page for myself (and maybe others in the future) and having an issue with a button that will show and hide weather alerts. You can view the page to see what I'm trying to do. (Sorry, I'm picking on FL, but they have a lot of alerts right now).
Page Source
JS Source
I have my alerts coming into an array and for each item, I need a button that will show and hide the alerts. My page source contains:
<div data-bind="foreach: alertsViewModel.features">
<div class="col-12">
<div class="alert alert-danger" role="alert">
<p>
<strong data-bind="text: properties.headline"></strong>
<button type="button" class="btn btn-link" data-bind="click: $root.showHideAlert">Show</button>
</p>
<div data-bind="attr: {id: properties.id}" style="display: none;">
<p data-bind="lineBreaks: properties.description"></p>
<p><strong>Instructions</strong></p>
<p data-bind="lineBreaks: properties.instruction"></p>
</div>
</div>
</div>
</div>
And my ViewModel looks like:
// ==================
// Alerts View Model
// ==================
var alertsViewModel = {
features: ko.observableArray([]),
hwoUrl: ko.observable(""),
hwoText: ko.observable(""),
showHideAlert: function(data, event){
alert('you clicked');
/*$('#hwo').toggle('slow',function(){
if ($('#showHwo').text() == "Show")
{
$('#showHwo').text("Hide");
}
else
{
$('#showHwo').text("Show");
}
});*/
}
};
ko.applyBindings(weatherViewModel, document.getElementById('weather-alerts'));
I have tried a few different methods and I can't get anything to work. (Thus the commented code and the alert). Which is strange, since I have done this a few times in the past with no issues. I'm sure it's something simple I missed. Any help would be appreciated.

Could it perhaps be because you used the weatherViewModel in your call to ko.applyBindings instead of alertsViewModel?
I think the $root in the button's bindings refers to weatherViewModel since that's the VM applied by ko.
Perhaps try changing the location of the function or simply use alertViewModel instead.

Related

Reading/Displaying different text files with one function in jQuery

I'm trying to build a notary of sorts. This notary will have different buttons and with the press of each button a different message of plain text will display in a custom div. I want all buttons to display different messages, but display them in the same div and when a different button is pressed, the former message will fade away and the new message will fade in.
I have a basic understanding of the jQuery.get() and this is my JS and HTML code that I've used to read/display one file in that custom div by clicking the button labeled "First":
function Answer() {
jQuery.get("file.txt", function(text) {
$(".answerBox").text(text);
})
};
<body>
<div class="content">
<div class="buttons">
<button class="button" type="button" onclick="Answer()">First</button>
<button class="button" type="button" onclick="Answer()">Second</button>
</div>
<div class="textBox">
<div class="answerBox">Click Any Question Button</div>
</div>
</div>
</body>
The issue comes when clicking the button labeled "Second", it reads the same file.txt and displays the same message. I am puzzled as to how I can expand this single function to encompass all 300 some odd buttons I will need and yes, each button will display a different message and will be tied to a different txt file. I really don't want to rewrite this small function 300 some times and I know there is a better way I just cannot find it.
So I've just asked this question, but I've been digging a bit more and I think I may have found something useful. I've rewritten my JS function and HTML as:
`function Answer(file) {
let list = [];
url = "files/" + file
list.push(url);
if (list.includes(url)) {
jQuery.get(url, function(text) {
$(".answerBox").fadeOut(() => {
$(".answerBox").text(text);
})
$(".answerBox").fadeIn(2000)
});
<div class="buttons">
<button class="button" type="button" onclick="Answer('file.txt')">First</button>
<button class="button" type="button" onclick="Answer('file1.txt')">Second</button>
</div>
`
This kinda solves my initial problem, but now I will need to make 300 instances of buttons in HTML. Is there any other way or is this the best I can do with what I know?

Recaptcha is working on mobile, but not on desktop

So, I have a website and I've added a recaptcha to the contact form. I've used this code for the recaptcha and the contact form on multiple websites before, and it worked perfectly on those. But, for some reason, on this website, the recaptcha isn't working on the desktop version of the site.
I've tried removing the old recaptcha and creating a new one. That didn't work.
I thought that maybe because I had a different style of formatting for the mobile version and the desktop version on the same page, that it might be causing an error. But, when I deleted one of the recaptchas, I had the same result - mobile was fine and desktop was not.
I tried it in different browsers to see if it was a chrome error, and even updated my chrome - it wasn't that either.
I checked to see if anyone else had a similar issue and they claimed that they were loading the recaptcha twice. But I checked my files and I can't see anywhere it would be loading other than the google api link in the header.
The header:
<script src='https://www.google.com/recaptcha/api.js' async defer></script>
</head>
The recaptcha section of my contact form:
<div class = "row d-none d-sm-none d-md-block">
<div class = "col-md-6">
<div class="g-recaptcha" id = "captcha" name = "captcha" data-sitekey="SITE_KEY_HERE" style = "text-align:left;"></div>
</div>
<div class = "col-md-6">
<div class = "submit" style = "text-align: right;">
<button id = "submitForm" type="submit" name = "submit" class="btn btn-lg">Send Message</button>
</div>
</div>
</div>
<div class = "row d-inline-block d-sm-inline-block d-md-none">
<div class = "col-sm-12 text-center">
<div class="g-recaptcha" id = "captcha" name = "captcha" data-sitekey="SITE_KEY_HERE" style = "text-align:center;"></div>
</div>
</div>
<div class = "row d-block d-sm-block d-md-none">
<div class = "col-sm-12 text-center">
<div class = "submit">
<button id = "submitForm" type="submit" name = "submit" class="btn btn-lg">Send Message</button>
</div>
</div>
</div>
I expected the user to be able to see a normal, recaptcha v2 'I am not a robot' checkbox, but I get:
ERROR for site owner: Invalid domain for site key
on the desktop version. It's working perfectly on the mobile version.
Update: I even tried making unique recaptcha keys for the desktop and mobile version, but that didn't work either.
SOLVED IT!!
I'm so sorry everyone.
Turns out that I had accidentally added two versions of the contact form and I didn't realize it. I wasn't updating the site key of the one visible on the desktop, and continued to edit the one version of the form that I thought I had.. so it kept showing as not valid.
I'm very very sorry everyone. I guess this means that it pays to read your code line by line sometimes..
If it's reloading for desktop twice that means could be issue with below code.
class = "row d-inline-block d-sm-inline-block d-md-none"
can you change class to "visible-xs" and see if it works?
Have you tried the explicit rendering as mentioned here - https://developers.google.com/recaptcha/docs/display#explicit_render
As stated in the link above, change your script tag to
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
async defer>
</script>
Then add a callback function to logically render the widget you want to (based on maybe screen size!) something like this.
<script type="text/javascript">
var onloadCallback = function() {
if (screen.width < 600)
grecaptcha.render('captcha-one', { //id of the first captcha needs to be captcha-one
'sitekey' : 'your_site_key'
});
else
grecaptcha.render('captcha-two', { //id of the second captcha needs to be captcha-two
'sitekey' : 'your_site_key'
});
};
</script>
Or you might actually just render both of them based on the other sample codes there in the mentioned link.

How to only select this textarea on click?

I need some help, I am stuck at this problem.
I have an api that I am calling, it brings in a list of information, I output the data into a textarea, on click I only want to select THIS textarea (not the others), and copy the content into my clipboard.
This is what I wrote:
function copyText() {
$(this).find('.api-text').select();
document.execCommand('copy');
}
But this is not working, when I replace this with '.api-text' and remove the find(), it selects ALL results with that class.
All the help is appreciated!
UPDATE:
Here is a jsFiddle with a rough example of what I am seeing.
https://jsfiddle.net/ax6na18u/
I found the solution after googling a suggestion from Rajesh on event.target or "(this)".
Rajesh's original code worked, just had to tie it in correctly in the HTML with $event.target:
<button class="btn btn-primary" (click)="copyText($event.target)">Copy to Clipboard</button>
The $event.target was the route of all evil!
As suspected, issue is in navigating to textarea.
First, your this is not pointing to button. Second, even if it was, you do not have an element with class api-txt in it.
You should try one of these approaches:
Go to nearest parent(li) that encapsulates both button and textarea and find textarea.
$(el).closest('li').find('.api-txt').select();
If the structure is going to remain like this where textarea and button will be on same level, you can search at this level.
$(el).siblings('.api-txt').select();
Sample
function copyText(el) {
//$(el).closest('li').find('.api-txt').select();
// or
$(el).siblings('.api-txt').select();
document.execCommand('copy');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<ul *ngIf="results">
<li *ngFor="let result of results | slice:0:9">
<a href="{{ result.latest }}" target="_blank">
{{ result.name }}
</a>
<textarea name="apiurl" class="api-txt">{{ result.latest }}</textarea>
<br>
<button class="btn btn-primary" onclick="copyText(this)">Copy to Clipboard</button>
</li>
</ul>

How to pass child object from ng-repeat to ng-click

I wish the previous questions had answered this problem, but it seems like I'm facing something different here, unless I'm missing something..
I understand ng-repeat creates a child object and from that we can call things such {{note.id}}.
My problem is living in my attempt to pass that note.id to ng-click
Below the code with attempts and errors.
<div class="col-lg-3" ng-controller="mainCtrl">
<div class="list-group">
<a href="#notes/{{note.id}}" class="list-group-item" ng-repeat="note in notes" >
{{note.title}}
<button ng-click="deleteNote(note.id)" class="btn btn-danger" >x</button>
</a>
</div>
</div
Inspecting the elements we have
<a href="#notes/5" class="list-group-item ng-binding ng-scope" ng-repeat="note in notes">
hello update
<button ng-click="deleteNote(note.id)" class="btn btn-danger">x</button>
</a>
And in case I try something that would look more like the angularjs sintax
<button ng-click="deleteNote({{note.id}})" class="btn btn-danger" >x</button>
In that case my element inspection brings up the right id ...
<button ng-click="deleteNote(5)" class="btn btn-danger">x</button>
Bu I have an error
Error: [$parse:syntax] http://errors.angularjs.org/1.2.19/$parse/syntax?p0=note.id&p1=is%20unexpected%2C%20expecting%20%5B%3A%5D&p2=14&p3=deleteNote(%7B%7Bnote.id%7D%7D)&p4=note.id%7D%7D)
I could not tell what the error is from the message or the link it takes me to
Syntax Error: Token 'note.id' is at column {2} of the expression [{3}] starting at [{4}].
code for the controller
.controller('mainCtrl',['$scope', 'notesFactory', function($scope, notesService){
$scope.notes = notesService.notesObjectInService;
$scope.addNote = function(){
if ($scope.title === "" ) {
return;
}
notesService.create({
title: $scope.title
body:$scope.body
});
$scope.title= '';
$scope.body= '';
};
$scope.deleteNote = function(id) {
notesService.delete(id);
notesService.getAll();
};
}])
http://jsfiddle.net/p2t1waxp/2/
Your first attempt
<button ng-click="deleteNote(note.id)" class="btn btn-danger">x</button>
actually works, doesn't it?
It just doesn't render the id when you inspect it, but it process it right in the controller, as you can see in the console
I'm sorry but turns out that the standard way to do things as supposed works just fine. ng-click(note.id) ..
I had not implemented completely my function yet because I was debugging with Chrome tools Inspecting the element and I expected that the id element should come up in the inspection. Once it didn't as I showed in the code above I assumed it was wrong. But by finishing implementing the service and the back end it actually works just fine.
It's something I didn't know about the developer tools. What i still don't understand is why the id doesn't show up in the html inspection like href="#notes/{{note.id}}" ....I am assuming it has to do with the {{}} notation, but I'd have no precise answer for it. If someone can explain it'd help with my knowledge of angular and html as I'm new to programming.
Thanks and sorry for silly mistake.

KnockoutJS: How to avoid running a viewmodel function on applyBindings?

I'm a little puzzled by some code I plunked together that doesn't behave quite as I'd expect.
I'm sure I'm doing something wrong (and given the late hour here, it might be something simple), but I'm looking for some clarity on why this happens.
I'm using:
jQuery 1.10.2
Knockout 2.3.0
Bootstrap 3.0.3
The Problem
I define a function in my ViewModel, which sets an observable to a certain value.
This is not called from anywhere else in my code.
I define a data-bind="click: AddAnnouncement" binding on a button that's part of a button group.
When ko.applyBindings(vm) is called, the AddAnnouncement function fires, giving me an undesired result long before I click on anything.
The Code in Question
Can be found in a JSFiddle at: http://jsfiddle.net/SeanKilleen/v8ReS/.
Essentially, I have the following JavaScript code:
var MyNamespace = MyNamespace || {
ViewModel: function(){
'use strict';
var self = this;
self.AddingAnnouncement = ko.observable(false);
self.AddAnnouncement = function(){
self.AddingAnnouncement(true);
};
self.Start = function(){
self.AddingAnnouncement(false);
};
self.Start();
}
};
var vm;
$(document).ready(function(){
'use strict';
vm = new MyNamespace.ViewModel();
ko.applyBindings(vm);
//do something with jQuery? Bind a VM?
});
My binding code is also pretty elementary (I thought):
<div class="container">
<div class="row">
<div class="btn-group">
<button type="button" class="btn btn-default"><abbr Title="Announcement" data-bind="click: AddAnnouncement()">A</abbr>
</button>
</div>
</div>
<div class="row" data-bind="visible: AddingAnnouncement() == true">
<h1>Add a new announcement</h1>
</div>
</div>
What I think it's doing
I think the code in question is doing the following:
Defining a namespace called MyNamespace (albeit probably not in the best way; this may be part of the problem?)
Defining a ViewModel object inside the namespace
Giving the ViewModel object an observable called AddingAnnouncment and a function called AddAnnouncement, which sets AddingAnnouncement to true.
Defines a Start method which ensures that AddingAnnouncement is set to false by default;
Calls the Start method as the last step in its initialization.
What am I Missing Here?
I think I'm not grasping some standard behavior of JavaScript or something about the way knockout binds models, but it seems like when applying the bindings, knockout executes all of the functions, even for the click bindings. However, that doesn't make sense to me and so I'm betting I'm wrong.
Someone enlighten me? Thanks!
Whooops! The answer to that question turned out to be right under my nose; indeed, all I had to do was write that entire darn question before I saw it. :)
The problem is with my binding:
<button type="button" class="btn btn-default"><abbr Title="Announcement" data-bind="click: AddAnnouncement()">A</abbr>
Note a very important distinction: AddAnnouncement(). The () matters quite a bit in this case.
When knockout assigns its binding, it does so by directly referencing what you enter. Since I entered AddAnnouncement(), it assigned the binding to the output of the function that had been run once, rather than the function itself which would be executed at a later time.
The best way to do it would have been to use AddAnnouncment, without paranetheses, like this:
<button type="button" class="btn btn-default"><abbr Title="Announcement" data-bind="click: AddAnnouncement">A</abbr>
This does not execute the function upon applying bindings.
While I forgot to avoid such a simple mistake, I hope it saves someone else time in the future. The working JSFiddle can be found at http://jsfiddle.net/SeanKilleen/v8ReS/4/.
We usually confuse when to use parentheses () when we bind View with ViewModel.
As when you bind AddAnnouncement function, you directly bind with function call like AddAnnouncement(). That why the AddAnnouncement function call when you bind using ko.applyBindings even though we didn't click the button, the function call already fire.
<div class="row">
<div class="btn-group">
<button type="button" class="btn btn-default">
<abbr Title="Announcement" data-bind="click: AddAnnouncement()">
A
</abbr>
</button>
</div>
</div>
so we change as below
<div class="row">
<div class="btn-group">
<button type="button" class="btn btn-default">
<abbr Title="Announcement" data-bind="click: AddAnnouncement">
A
</abbr>
</button>
</div>
</div>
working jsfiddle

Categories