AJAX call from on-click fails - node.js

I have a bizarre error where ajax call in a on('click') listener executed from an external trigger('click') call will fail, but execute correctly if the on('click') listener is called via a regular user mouse-click.
HTML:
....
<div class="navbar-collapse collapse">
<ul class="navbar-nav mr-auto navbar-right">
<li class="nav-item dropdown active">
<a class="nav-link dropdown-toggle" id="nbd" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Graphs<span class="sr-only">(current)</span></a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item ycai" id="d1" href="#" data-type="1">Graph 1</a>
<a class="dropdown-item ycai" id="d2" href="#" data-type="2">Graph 2</a>
</div>
</li>
</ul>
<form class="form-inline my-2 my-lg-0 navbar-right">
<div id="nbsa"></div>
<input class="form-control mr-sm-2" id="nbpc" placeholder="Postal Code" style="width:140px" aria-label="Postal Code" type="search">
<select class="form-control mr-sm-2" id="nbs">
<option value="1" selected="true">Average-case</option>
<option value="2">High-case</option>
</select>
</form>
</div>
JS:
<script>
var dataV = {
gtype:2,
nbpc: null,
nbs: null
};
$('#nbpc').keyup(function(e) {
if (e.keyCode === 13) {
e.preventDefault();
dataV.nbpc = $(this).val();
switch(dataV.gtype) {
case 1:
$('#d1').trigger('click');
break;
case 2:
$('#d2').trigger('click');
break;
}
}
});
$('#nbtop').on('click', '.ycai', function(e) {
console.log('In Click...',$(this).attr('data-type'));
e.preventDefault();
var i=0, rval;
if (!$(this).hasClass("dislink")) {
$(this).addClass("dislink");
dataV.nbpc = $('#nbpc').val();
dataV.nbs = $('#nbs').val();
dataV.gtype = +$(this).attr('data-type');
console.log(dataV);
if (dataV.gtype<8 && dataV.nbpc == "") {
alert('Postal Code Cannot Be Blank...');
$('.dislink').removeClass('dislink');
$('#nbpc').focus();
return;
}
$.ajax({
type: 'POST',
url: '/climG',
dataType: 'json',
data: dataV
}).fail(function(jqXHR, textStatus, error) {
console.log(textStatus);
console.log(error);
alert('Error on Graph');
}).done(function(res) {
...
});
Scenario #1: If I add the postal-code in (#nbpc) first (without hitting Enter) and then select a graph (say Graph 2), the ajax call goes thru and executes perfectly.
Scenario #2: If I add the postal-code in (#nbpc) and then hit Enter, which defaults to Graph 2 and then executes the $('#d2').trigger('click'); via the $('#nbpc') keyup listener, then the ajax call will fail with no error message.
What is interesting are the node.js console lines.
In Scenario #1, the console log lines are perfect:
Console Log Line 1
...
Console Log Line N
POST /climG 200 378.344ms - 1872
In Scenario #2 (error), the console log lines are very strange:
GET /? 304 192.332 ms - -
POST /climG - - ms - -
Console Log Line 1
...
Console Log Line N
So in Scenario #2, an extra GET /? is generated for some unknown-to-me reason, and the /climG call quickly returns a failure, yet the actual /climG server-side function continues to execute (evidenced by the console log lines after the POST message). I have no clue what's happening. Thx.

the form element will default to GET as its action - if you are not using the form to submit your data, you can safely remove this tag.
It's likely being triggered by pressing the enter key - before you even get a chance to execute the JS.
In other words, change
<form class="form-inline my-2 my-lg-0 navbar-right">
<div id="nbsa"></div>
<input class="form-control mr-sm-2" id="nbpc" placeholder="Postal Code" style="width:140px" aria-label="Postal Code" type="search">
<select class="form-control mr-sm-2" id="nbs">
<option value="1" selected="true">Average-case</option>
<option value="2">High-case</option>
</select>
</form>
to
<div class="form-inline my-2 my-lg-0 navbar-right">
<div id="nbsa"></div>
<input class="form-control mr-sm-2" id="nbpc" placeholder="Postal Code" style="width:140px" aria-label="Postal Code" type="search">
<select class="form-control mr-sm-2" id="nbs">
<option value="1" selected="true">Average-case</option>
<option value="2">High-case</option>
</select>
</div>

Related

Semantic UI: get current dropdown search input text

I'm trying to filter some results I will get from an api using Semantic UI's dropdown search component.
The issue is that I don't know how to get the text I'm typing in the input field
The dropdown search I have:
<div class="ui fluid search selection dropdown" id="user-dropdown">
<input id="user-dropdown-input" name="country" type="hidden">
<i class="dropdown icon"></i>
<div class="default text">Search...</div>
<div class="menu" id="user-dropdown-menu">
<div class="item" data-value="af">
<span class="description">123</span>
<span class="text">User123</span>
</div>
<div class="item" data-value="af">
<span class="description">123</span>
<span class="text">User123</span>
</div>
<div class="item" data-value="af">
<span class="description">123</span>
<span class="text">User123</span>
</div>
</div>
</div>
How dropdown is initialized:
$(document).ready(function () {
$('.ui.dropdown').dropdown({
clearable: true,
fullTextSearch: true
});
});
What I tried:
$('#user-dropdown').on('keyup', function () {
let input = $('#user-dropdown');
console.log('Val: ' + input.dropdown().val());
// also tried: $('#user-dropdown-input').val()
// $('#user-dropdown-input').html()
// $('#user-dropdown-input').text()
});
Basically what I want is if I type "abc" to print the value "abc" into the console, but I don't know how to get that value.
what worked for me was searching the input used for search that looks like:
<input class="search" autocomplete="off" tabindex="0">
and I added a type to this input
document.getElementsByClassName('search').type = 'text';
and then got the value by class on keyup
$('.search').keyup(function() {
console.log($(this).val());
});

Jasmine test error - TypeError: Cannot read property 'hide' of undefined

i am using in .html & BSModalService, BSModalRef in component to display the pop-up modal open and close. getting error in Jasmine on testing the modalRef.hide() as --> TypeError: Cannot read property 'hide' of undefined
component.html
<div class="col col-md-2">
<button type="button" class="btn border btn-basic" (click)="openModal(userModal,1)"
style="border-radius: 50%">Search</button>
</div>
<ng-template #userModal>
<div class="modal-header">
<h4 class="modal-title pull-left">Search User</h4>
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="row" style="margin-bottom:15px">
<div class="col-sm-12">
<input type="text" class="form-control" placeholder="Search" [(ngModel)]="searchText"
[ngModelOptions]="{standalone: true}" (ngModelChange)="searchUser($event.target.value)">
</div>
</div>
<div class="list-group scrollbar">
<a class="list-group-item" *ngFor="let usr of users" [class.active]="usr===selectedUsr"
(click)="setIndexUser(usr)">{{usr.First_Name + ' ' + usr.Last_Name}}</a>
</div>
</div>
<div class="modal-footer text-right">
<button class="col col-md-2 btn-basic" (click)="selectUser()">Ok</button>
<button class="col col-md-2 btn-basic" (click)="cancelUser()">Cancel</button>
</div>
</ng-template>
component.ts
//open the User Pop-Up to select User.
openModal(template: TemplateRef<any>, type: number) {
if (type === 1) {
this.userService.getUserList().subscribe((res) => {
this.users = res.Data as User[];
this.modalRef = this.modelService.show(template);
},
(error) => {
this.toastr.error(error);
});
}
}
//cancel pop-up of selecting user.
cancelUser(){
this.modalRef.hide();
this.selectedUsr=null;
}
component.spec.ts
it('call cancel user', () =>{
component.cancelUser();
expect(component.modalRef.hide);
expect(component.selectedUser).toBeNull;
});
image
Expected results on testing the cancelUser() method, should be success, but failing with error --> TypeError: Cannot read property 'hide' of undefined
Since your test is more associated with html , so its better if you test is via click event rather than calling openModal() before cancelUser.
Try by putting ids in html buttons such as
<div class="col col-md-2">
<button type="button"
class="btn border btn-basic"
id="open-btn"
(click)="openModal(userModal,1)"
style="border-radius: 50%">Search</button>
</div>
....
.....
<div class="modal-footer text-right">
<button class="col col-md-2 btn-basic" (click)="selectUser()">Ok</button>
<button id="cancel-btn" class="col col-md-2 btn-basic" (click)="cancelUser()">Cancel</button>
</div>
</ng-template>
and in spec,
it('call cancel user and set "selectedUser" as null', () =>{
spyOn(component.modalRef,'hide').and.callThrough();
spyOn(component,'cancelUser').and.callThrough();
const openBtn = fixture.nativeElement.querySelector('#open-btn');
openBtn.click();
fixture.detectChanges();
const cancelBtn = fixture.nativeElement.querySelector('#cancel-btn');
cancelBtn.click();
expect(component.modalRef.hide).toHaveBeenCalled();
expect(component.cancelUser).toHaveBeenCalled();
expect(component.selectedUser).toBeNull();
});
The modalRef will not exist unless you call the openModal method. Try calling this method before calling cancelUser.

EJS just outputs the first found user in some cases

I'm Using mongoDB and ejs to display all my users in a table. The table also has some action buttons to delete the user and to change the users role. The buttons open a popup to change the role or to confirm the deletion. But EJS doesn't pass the users info into the popup. It works totally fine in the table, but not in the popup.
My EJS User Table with the Role change Popup:
<tbody>
<%users.forEach(function(users){%>
<tr>
<td><%=users.name%></td>
<td><%=users.username%></td>
<td><%=users.createdAt%></td>
<td><span class="badge label-table badge-<%=users.role%>"><%=users.role%></span></td>
<td><span class="badge label-table badge-<%=users.verifyEmailToken%>"><%=users.verifyEmailToken%></span></td>
<td>
<button type="submit" class="btn btn-xs btn-success" data-toggle="modal" data-target="#con-close-modal" name="changeRoleButton"><i class="remixicon-user-settings-line"></i></button>
</td>
</tr>
<div id="con-close-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display: none;">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Change <%=users.name%> Role</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<form class="" action="/changeuserrole" method="post">
<div class="modal-body p-4">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label for="field-1" class="control-label">User Role</label>
<select class="form-control" name="role">
<option>Partner</option>
<option>Admin</option>
<option>User</option>
</select>
</div>
<button type="submit" value="<%=users._id%>" name="userroleid" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<%});%>
</tbody>
Here is my app.js where I search for the users and pass them to EJS:
app.get("/users", function(req, res) {
if (req.isAuthenticated() && req.user.role === "Admin") {
User.find({}, function(err, foundUsers) {
if (err) {
console.log(err);
} else {
res.render("users", {
users: foundUsers,
name: req.user.name.replace(/ .*/, ''),
email: req.user.username,
});
}
});
} else {
res.redirect("/login");
}
});
All the <%=users...%> tags work inside the table, but not inside the popup divs. Inside the Popup it just displays the information from the first user in the Database, which is super strange.
I would be very thankful for any kind of help. Thanks!
Your ejs code is good. I think that the problem is the id of each modal.
For each user you generate a modal with id="con-close-modal", So all your modals have the same id. As a result, every submit button (all of them have the same data-target="#con-close-modal"), triggers the same modal, probably the first one.
I recommend you, give each modal a unique id like
<div id="<%= users._id %>" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display: none;">
and give each submit button the right data-target attribute
<button type="submit" ... data-target="#<%= users._id %>"...></button>
I may be wrong, but since the popup is displaying only the info from the first user, you may need the specific user id to be apart of the link. I had a similar issue with a project, except it was a link to a new view instead of a pop up.
I hope this documentation may be of some help
https://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html

Pass Data from MongoDB to Bootstrap Modal Using EJS

I'm building an express app which comprises ejs and bootstrap for rendering views, node and express for server side and mongodb for storage.
I have a form (contained in a modal) in which I can post data. After submitting the form, the values are saved to mongodb.
Here's the snippet for edit:
<td><%= cats.image %></td>
<td><a type="button" data-toggle="modal" data-target="#editCategory" href="#">Edit</a>
// without using the modal above, I'll have to create a new page to edit only one item
// I think it's a waste of page space and bad UX, so I am going with the modal
<!--<a href="/admin/categories/edit-category/<%= cats._id %>">--> // I want to get the values from this id and show in the modal
<!--<button type="button" class="btn btn-primary btn-sm">Edit</button>-->
<!--</a>-->
</td>
When I click on the edit button, I want to get the id of the item and retrieve it's values to display in the modal.
Here's the full modal view:
<!-- Edit Category Modal -->
<div class="modal fade" id="editCategory" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form action="/admin/categories/edit-cateory/<%= %>" method="post" enctype="multipart/form-data">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button>
<h4 class="modal-title" id="myEditCategoryLabel">Edit Category</h4>
</div>
<div class="modal-body">
<div class="form-group">
<label>Title</label>
// this value should come from the category id but don't know how to pass it
<input value="<%= %>" name="title" class="form-control" type="text" placeholder="Category Title"> <br>
<label>Upload Image</label>
<input class="form-control selImg" type="file" accept="image/*" onchange="showImage.call(this)">
<img src="#" class="imgPreview" style="display: none; height: 100px; width: 100px">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save Edits</button>
</div>
</form>
</div>
</div>
</div>
How can I pass the data from mongodb to the modal using ejs?
Any help will be much appreciated.
Store your ID somewhere: hidden inputs or data-attributes.
<tr data-id="<%= cats.id %>">
<td><%= cats.image %></td>
<td>
<a type="button" class="edit" href="#">Edit</a>
</td>
</tr>
Then, create an onclick event handler such that, upon clicking on the button,
it will fetch the extra data via AJAX and open the modal dynamically after you get a response.
$('a.edit').click(function (e) {
e.preventDefault();
var tr = $(this).closest('tr'),
modal = $('#editCategory');
// make AJAX call passing the ID
$.getJSON('/admin/categories/get-cateory/' + tr.data('id'), function (data) {
// set values in modal
modal.find('form').attr('action', '/admin/categories/get-cateory/' + tr.data('id') );
modal.find('[name=title]').val( data.title );
// open modal
modal.modal('show');
});
});
You will need to create a route for fetching a single object (I'll leave that to you)
app.get('/admin/categories/get-cateory/:id', function (req, res) {
// TODO: fetch data using passed ID
// once you have the data, simply send the JSON back
res.json(data);
});

Asyc Error on IE protractor

I'm currently trying to test the following pice of code:
<footer class="footer">
<div class="container">
<div class="row">
<form subscribe-directive="" ng-controller="SubscribeController" class="form col-xs-12 col-sm-8 col-md-6 col-lg-4 ng-scope ng-dirty ng-valid ng-valid-email">
<h3 class="footer__title text-uppercase margin-bottom-25">Sign up!</h3>
<div class="row">
<div class="col-sm-8 col-xs-7">
<input type="email" class="form-control ng-touched ng-dirty ng-valid ng-valid-email" ng-disabled="working || subscription.done" placeholder="Email Address" ng-model="subscription.email"> </div>
<div class="col-sm-4 col-xs-5">
<button ng-click="submit(subscription.email)" ng-disabled="working || !subscription.email || subscription.done" ng-class="{'working': working}" class="btn btn--inverse btn-red form-control" type="submit" disabled="disabled"> <span ng-bind-html="submitBtn" class="ng-binding">SUBSCRIBE</span> </button>
</div>
</div>
<p ng-show="callback_message" class="msg ng-binding ng-hide" ng-bind-html="callback_message"></p>
</form>
<nav class="col-xs-12 col-md-6 col-lg-offset-2">
<ul class="nav navbar-nav navbar-right margin-bottom-25">
<li>Press
</li>
<li>News
</li>
<li>Contact Us
</li>
<li><a target="_blank" href="//test.com/en">Test Project</a>
</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="footer__social-icon"> <a target="_blank" href="https://www.facebook.com/"><i class="fa fa-facebook"></i></a> </li>
<li class="footer__social-icon"> <a target="_blank" href="https://twitter.com/"><i class="fa fa-twitter"></i></a> </li>
<li class="footer__social-icon"> <a target="_blank" href="https://www.youtube.com/user/"><i class="fa fa-youtube"></i></a> </li>
</ul>
</nav>
</div>
<div class="row">
<div class="col-xs-12 col-sm-6 copy"> Privacy Policy
<br> Copyright © Test-inc. All Rights Reserved. </div>
<div class="col-xs-12 col-sm-6 text-right copy">
<br> Microsoft Ventures. Supported by Microsoft Ventures London Accelerator </div>
</div>
</div>
</footer>
But when I do the following actions:
it('User should see a message that he has already been added to the campaing when entering the same email twice', function () {
browser.executeScript("window.scrollBy(0,10000)");
basePage.email.sendKeys('bruno#test.com');
basePage.subscribe.click().then(function () {
browser.sleep(7000);
basePage.confirmMessage('Contact already added to target campaign');
});
On my basePage I've:
//this.email = element(by.model('subscription.email'));
this.email = element(by.xpath('/html/body/footer/div/div[1]/form/div/div[1]/input'));
this.waitlistBtn = element.all(by.binding('submitBtn'));
this.subscribe = element(by.buttonText('SUBSCRIBE'));
I keep getting the follwing error (I'm running it against BrowserStack):
Failures:
1) New Landing page module verification --> User should be correctly added to the update list
Message:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Stack:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
at Timer.listOnTimeout (timers.js:110:15)
Here's my part of my configuration:
{
name: testName,
browserName: 'IE',
browser_version: '11.0',
os: 'Windows',
os_version: '8.1',
resolution: '2048x1536',
'browserstack.user': browserstackUser,
'browserstack.key': browserstackKey,
'browserstack.debug': 'true',
'browserstack.selenium_version': '2.45.0',
'browserstack.ie.driver': '2.44',
ignoreProtectedModeSettings: true
}
onPrepare: function () {
jasmine.getEnv().addReporter(reporter);
browser.driver.manage().window().maximize();
global.dvr = browser.driver; //variable to call selenium directly
global.isAngularSite = function (flag) {
browser.ignoreSynchronization = !flag; //This setup is to configure when testing non-angular pages
};
//browser.manage().timeouts().pageLoadTimeout(90000);
browser.manage().timeouts().implicitlyWait(100000);
}
I must clarify that it does not happen with other test for IE, it just happens with this part that's located at the footer of the page.
Can you please help? do you have any suggestion? or what can you see that I'm doing wrong?
Thanks.-
Since I had many problems to avoid the ASync, I've decided that for this particular scenario to avoid IE! I've done the following:
it('User should see a message that he has already been added to the campaing when entering the same email twice', function () {
browser.getCapabilities().then(function (capabilities) {
browser = capabilities.caps_.browserName;
platform = capabilities.caps_.platform;
}).then(function () {
console.log('Browser:', browser, 'on platform', platform);
if (browser == 'internet explorer') {
console.log('IE Was avoided for this test.');
} else {
basePage.email.sendKeys('bruno#test.com');
console.log('Mande el mail');
basePage.subscribe.click().then(function () {
basePage.confirmMessage('Contact already added to target campaign');
});
}
});
});
Please, if anyone reading this comes up with a better solution, please post.

Resources