why document.ready() dont work after fetching data with ajax in div element? - document-ready

I have a problem with this jQuery function that i use for add and remove box to my page:
jQuery(document).ready(function($){
$('.my2-form .add2-box').click(function(){
var n = $('.text2-box').length + 1;
if( 12 < n ) {
alert('you can't make more than 12 box');
return false;
}
$.post('showselectdatearray.php', { type: 'months', year: 93}, function(result) {
var box_html = $('<p class="text2-box" style="padding: 0px; margin: 0px;"><label for="box' + n + '"><span class="box2-number">' + n + ' </span></label> <input type="text" name="boxes[]" value="" id="box' + n + '" size="8" /> '+resultremoveitem</p>');
box_html.hide();
$('.my2-form p.text2-box:last').after(box_html);
box_html.fadeIn('slow');
box_html.css( 'background-color', '#48b973' );
return false; });
});
$('.my2-form').on('click', '.remove2-box', function(){
$(this).parent().css( 'background-color', '#FF6C6C' );
$(this).parent().fadeOut("slow", function() {
$(this).remove();
$('.box2-number').each(function(index){
var p =index+1;
var str = p+'\xa0\xa0\xa0\xa0';
$(this).text( str );
});
});
return false;
});
$('.my2-form p.text2-box:last').css( 'background-color', '#FFFFFF' );
});
then i use from above script in my code this way:
<div id="showresult12" class="my2-form">
<div>
<input type='button' id='AddMore' name='AddMore' value='add box' class='add2-box' />
</div>
<div style="float: right;" class="scroll10">
<p class="text2-box" style="padding: 0px; margin: 0px;">
<label for="box"><span class="box2-number">1 </span></label>
<input type="text" name="boxes[]" value="" id="box" size="8" />
<?php Show_Select_Date_Array("months",0,0,0,93) ?>
</p>
</div>
</div>
Every thing is OK now and when user click on "add box" button, another box appear and when user click on "remove it" button, one box remove.
I have another part -part2- under this code that fetch from database. after user click on "edit" button that is locate in -part2-, information fetch from database to boxes with php code, but my add and remove button don't work at all. my information fetch with Ajax and replace in new boxes. the 'showresult12' div id completely load again with same data.
what is the problem after replacing div element?!
and what change i must do in my jquery code that it work after div load again?

In your 'var box_html=...' line, there looks to me to be a bit of a syntax error:
'+resultremoveitem</p>');
where 'result' isn't used properly. Should it be something like +result+'<a href... ?
EDIT: If as you say your 'showresult12' div is being emptied and then refilled again dynamically, then your 'add' event handler will always be invalid - it's not using delegation like the 'remove' one.
$('.my2-form').on('click', '.remove2-box', function(){ - this is OK, as it will work for dynamically-created .remove2-box elements.
$('.my2-form .add2-box').click(function(){... - however this is not, as it is only valid for .add2-box elements that were present in the DOM when this handler was created.
So in this scenario, the 'add' hander will not work following your AJAX PHP call.
Furthermore, if you are actually going further and removing and re-adding the .my2-form div (#showresult12) itself, then neither of the handlers will work - you would have to do something like $('body').on('click', '.my2-form .add2-box', function() {....

thanks for your answer but my result fetch from file with name "showselectdatearray.php". it is not important you can remove this
$.post('showselectdatearray.php', { type: 'months', year: 93}, function(result) {
AND result variable in : '+result<a href="#"

Related

Not able to click on a button, when isExisting is true; isDisplayedInViewport true, but waitForDisplayed timed out for the element in WebdriverIO

The script fails with this error.
Error: element (".WONDERESC") still not displayed after 30000ms
Tried different combinations for Xpath, (relative, fixed, text()} and CSS selectors, but the button not clicked. The sign-in button div in the code block:
<div>
<div class="WONDERBSC" role="form">
<div>
<div class="WONDERJ1B" data-automation-id="userName">
<div class="TOM-Label WONDERP1B"
title="Username">Username</div>
<input type="text"
class="TOM-TextBox WONDERM1B" aria-label="Username">
<button type="button" class="TOM-Button WONDERN1B"/>
</div>
</div>
<div>
<div class="WONDERJ1B"
data-automation-id="password">
<div class="TOM-Label WONDERP1B" title="Password">Password</div>
<input type="password" class="TOM-PasswordTextBox WONDERM1B" aria-label="Password">
<button type="button" class="TOM-Button WONDERN1B"/>
</div>
</div>
<button type="button"
class="WONDERESC"
data-automation-id="goButton">Sign In</button>
</div>
</div>
Kindly suggest the workarounds - the other conditions are also meeting- visibility-true, display-block, opacity not zero.
Thanks,
Tan
If element is not displayed then you have to make it visible
Case 1
The element takes some time to be visible automatically
In this case, you can use webdriver wait
WebDriverWait wait = new WebDriverWait(driver,Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("xxxx"));
Case 2
Element is visible after some interaction like mouse movement or click or type
Then you have to perform that action with selenium. For mouse movements you can use action classes
As it was told in the comments, you are using invalid selectors.
Assuming you have the latest WebdriverIO v6, the following works like a charm
// open page
browser.url('https://impl.workday.com/wday/authgwy/accenture_dpt2/login.htmld?redirect=n')
const form = $('[data-automation-id="authPanel"] [role="form"]')
// make sure form is visible
expect(form).toBeVisible()
// define form data
const formData = [{
id: 'userName', value: 'test'
}, {
id: 'password', value: 'password'
}]
// fill form
formData.forEach(f => {
form.$(`[data-automation-id="${f.id}"] input`).setValue(f.value)
})
// submit form
form.$('button[data-automation-id="goButton"]').click()
// make sure error message exist
const errorMessage = $('[data-automation-id="alertMessage"]')
expect(errorMessage).toBeVisible()
expect(errorMessage).toHaveTextContaining('Invalid user name or password')

Increment/decrement button issue

Html markup:
<td data-title="Qty" class="vertical_text quantity">
<button type="button" class="quantity-left-minus btn btn-danger btn-number" data-type="minus" data-field="">
<span class="glyphicon glyphicon-minus"></span>
</button>
<input class="cart-input" type="text" name="qty[]" id="quantity" value="<?php echo $cart['qty']; ?>">
<button type="button" class="quantity-right-plus btn btn-success btn-number" data-type="plus" data-field="">
<span class="glyphicon glyphicon-plus"></span>
</button>
</td>
Script code:
$(document).ready(function() {
var quantitiy = 0;
$('.quantity-right-plus').click(function(e) {
// Stop acting like a button
e.preventDefault();
// Get the field name
var quantity = parseInt($('#quantity').val()); //10
// If is not undefined
$('#quantity').val(quantity + 1);
// Increment
});
$('.quantity-left-minus').click(function(e) {
// Stop acting like a button
e.preventDefault();
// Get the field name
var quantity = parseInt($('#quantity').val());
// If is not undefined
// Increment
if (quantity > 1) {
$('#quantity').val(quantity - 1);
}
});
});
I'm using bootstrap and Codeigniter. In my shopping cart table there is two button (-)button and (+)button with input field. if there is is single row in my table and when I'm clicking on the (-) button / (+) button the value of input field increment/decrement properly. but my problem is if there is two or more row in my table if I'm clicking to second row or third row (-)button/(+)button the value of first row input field increment/decrement. And when I'm refreshing the page the previous value shows up again. Help to increment/decrement button work properly and the value of input field should be update properly. Thank you.

How can I compose a VM into a view within an Aurelia validation renderer

I'm trying to use the aurelia-validation plugin to perform validation on a form. I'm creating a custom validation renderer that will change the color of the input box as well as place an icon next to the box. When the icon is clicked or hovered, a popup message appears that will display the actual error message.
Currently, I'm rendering all of this in code manually in the renderer, but it seems like it would be nice to have the html for all of this defined in an html file along with the associated js file to handle the click and hover on the icon. IOW, encapsulate all the error stuff (icon with popup) in a View/ViewModel and then in the render() of my validation renderer, somehow just compose a new instance of this just after the element in question.
Is this possible to do? I've seen how to use <compose></compose> element but I really am trying to avoid having to add that to all of my forms' input boxes.
This is what I currently have in my renderer:
import {ValidationError, RenderInstruction} from 'aurelia-validation'
export class IconValidationRenderer {
render(instruction){
//Unrender old errors
for(let {result, elements} of instruction.unrender){
for(let element of elements){
this.remove(element, result);
}
}
//Render new errors
for(let {result, elements} of instruction.render){
for(let element of elements){
this.add(element, result)
}
}
}
add(element, result){
if(result.valid)
return
//See if error element already exists
if(element.className.indexOf("has-error") < 0){
let errorIcon = document.createElement("i")
errorIcon.className = "fa fa-exclamation-circle"
errorIcon.style.color = "darkred"
errorIcon.style.paddingLeft = "5px"
errorIcon.id = `error-icon-${result.id}`
errorIcon.click = ""
element.parentNode.appendChild(errorIcon)
element.classList.add("has-error")
element.parentNode.style.alignItems = "center"
let errorpop = document.createElement("div")
let errorarrow = document.createElement("div")
let errorbody = document.createElement("div")
errorpop.id = `error-pop-${result.id}`
errorpop.className = "flex-row errorpop"
errorarrow.className = "poparrow"
errorbody.className = "flex-col popmessages"
errorbody.innerText = result.message
console.log("Computing position")
let elemRec = errorIcon.getBoundingClientRect()
let elemH = errorIcon.clientHeight
errorpop.style.top = elemRec.top - 10 + "px"
errorpop.style.left = elemRec.right + "px"
errorpop.appendChild(errorarrow)
errorpop.appendChild(errorbody)
element.parentNode.appendChild(errorpop)
}
}
remove(element, result){
if(result.valid)
return
element.classList.remove("has-error")
let errorIcon = element.parentNode
.querySelector(`#error-icon-${result.id}`)
if(errorIcon)
element.parentNode.removeChild(errorIcon)
//Need to remove validation popup element
}
}
Thanks for any help you can offer.
P.S. At this point, I am not implementing a click or hover like I mentioned -- that is something that I would like to do but I'm not even sure how at this point. Would be more straight forward if I can compose a VM.
EDIT
I was pointed to this article by someone on the Aurelia Gitter channel. I've tried implementing the TemplatingEngine but clearly I'm not going about it the right way. Here's what I have.
add-person-dialog.js //VM that has form with validation
import {TemplatingEngine,NewInstance} from 'aurelia-framework'
import {ValidationController} from 'aurelia-validation'
import {IconValidationRenderer} from './resources/validation/icon-validation-renderer'
export class AddPersonDialog {
static inject = [NewInstance.of(ValidationController),TemplatingEngine]
constructor(vc, te){
this.vc = vc
this.vc.addRenderer(new IconValidationRenderer(te))
}
icon-validation-renderer.js
//Plus all the other bits that I posted in the code above
constructor(te){
this.te = te
}
add(element, result){
if(result.valid) return
if(element.className.indexOf("has-error") < 0 {
//replaced there error icon code above with this (as well as a few different variations
let test = document.createElement("field-error-info")
element.parentNode.appendChild(test)
this.te.enhance({element: test})
}
}
field-error-info.html
<template>
<require from="./field-error-info.css" ></require>
<i class="fa fa-exclamation-circle" click.delegate="displayMessage = !displayMessage" mouseenter.delegate="displayMessage = true" mouseleave.delegate="displayMessage = false"></i>
<div show.bind="displayMessage" class="flex-row errorpop" style="left:300px">
<div class="poparrow"></div>
<div class="flexcol popmessages">Message 1</div>
</div>
</template>
Ultimately, <field-error-info></field-error-info> gets added to the DOM but doesn't actually get rendered. (Incidentally, I also tried adding <require from='./elements/field-error-info'></require> in the add-person-dialog.html.
You could create a form control custom element that encapsulates the error icon and tooltip logic. The element could expose two content projection slots to enable passing in a label and input/select/etc:
<template>
<div validation-errors.bind="errors"
class="form-group ${errors.length ? 'has-error' : ''}">
<!-- label slot -->
<slot name="label"></slot>
<!-- input slot -->
<slot name="input"></slot>
<!-- icon/tooltip stuff -->
<span class="control-label glyphicon glyphicon-exclamation-sign tooltips"
show.bind="errors.length">
<span>
<span repeat.for="errorInfo of errors">${errorInfo.error.message}</span>
</span>
</span>
</div>
</template>
Here's how it would be used:
<template>
<require from="./form-control.html"></require>
<form novalidate autofill="off">
<form-control>
<label slot="label" for="firstName" class="control-label">First Name:</label>
<input slot="input" type="text" class="form-control"
value.bind="firstName & validateOnChange">
</form-control>
<form-control>
<label slot="label" for="lastName" class="control-label">Last Name:</label>
<input slot="input" type="text" class="form-control"
value.bind="lastName & validateOnChange">
</form-control>
</form>
</template>
Live example: https://gist.run/?id=874b100da054559929d5761bdeeb651c
please excuse the crappy tooltip css

knockout model - observable empty in IE 11

I have a set of input fields that are used to set search params, passed to the controller via knockout and ajax upon pressing the 'search' button.
Each input is bound to a property in a viewModel - each one is a ko.observable().
I have code bound to the keyup event for these fields that will invoke the same search operation when the return key is pressed.
In Chrome, this works fine, but in IE(11) this is never passed!
I have also noted that if I press the tab key to go to the next field, and THEN press return, the search parameter I expect is now populated.
Any ideas? at my wits end...
How do I do the same as pressing the tab key in code, without pressing the tab key?
I think IE's handling of the .change() event in jquery might be different to everyone else...
edited to include sample code - js:
var searchVm = function () {
var self = this;
self.reference = ko.observable("");
self.postCode = ko.observable("");
self.description = ko.observable("");
self.doSearch = function () {
var date = {
Ref: self.reference(),
PostCode: self.postCode(),
Desc: self.description()
};
// do the ajax call to controller
// ... etc ...
}
}
$('.searchField').keyup(function(e) {
if (e.which===13) {
$('#btnSearch').click();
}
});
the page:
<div class='indexRow'>
<label>Reference:</label>
<input type='text' class='searchField' data-bind="value: reference" />
</div>
<div class='indexRow'>
<label>PostCode:</label>
<input type='text' class='searchField' data-bind="value: postCode" />
</div>
<div class='indexRow'>
<label>Description:</label>
<input type='text' class='searchField' data-bind="value: description" />
</div>
<button data-bind='click: doSearch'>Search</button>

Empty TabContainer in Dojo Dialog

The problem is that the TabConainer inside the Dialog is empty after opening although selected="true" is given (see the screenshot below). The content is called with dojo/html html.set(node, contentHTML, {parseContent: true});
When changing the tab by clicking on another one the content appears and the class "dijitVisible" is set for this div as it should be from the beginning. The attribute nested="true" is necessary since otherwise three select bars are shown over the tabContainer.
What can I do so that the content appears from the start on?
<div data-dojo-type="dijit/Dialog" id="formDialog" data-dojo-id="formDialog" title="Edit member data">
<div id="formContent" class="dijitDialogPaneContentArea" data-dojo-attach-point="formContent">
</div>
</div>
Update:
Here is the whole javascript for getting the content
getForm = function(formID, urlAction){
var contentHTML;
var xhrArgs = {
url: urlAction,
handleAs: "text",
load: function(data){
contentHTML = data;
},
error: function(error){
contentHTML = text_error_unexpected + ": " + error;
},
handle: function(error, ioargs){
var node = dom.byId(formID);
html.set(node, contentHTML, {parseContent: true});
}
}
var deferred = dojo.xhrGet(xhrArgs);
};
Update 2:
This is the content that gets called and inserted in the above div "formContent" (I thought I make the description as simple as possible and lost some details on the way)
<div id="form" data-dojo-type="dijit/form/Form" data-dojo-attach-point="form" encType="multipart/form-data" action="#">
<div style="width: 450px; height: 370px;">
<div data-dojo-type="dijit/layout/TabContainer" nested="true">
<div data-dojo-type="dijit/layout/ContentPane" title="Personal data" selected="true">
Content 1
</div>
<div data-dojo-type="dijit/layout/ContentPane" title="Detailed data">
Content 2
</div>
<div data-dojo-type="dijit/layout/ContentPane" title="Contact data">
Content 3
</div>
</div>
</div>
</div>
Have you tried calling either dialog.resize() or tabcontainer.layout() after adding it to the dialog?
I am not sure as to how the code below will place contents inside the first ContentPane (title="Personal data"). I am assuming that the parameter formID = "form"
html.set(node, contentHTML, {parseContent: true});
I can suggest an alterantive.
Use an id with the content pane as shown below.
<div id="content1" data-dojo-type="dijit/layout/ContentPane" title="Personal data" selected="true">
Content 1
</div>
Then use dijit/registry to get the contentpane widget in the handle function call as shown below.
handle: function(error, ioargs){
var content= registry.bId(formId); // over here formId = "content1"
content.set("content","<p>This is content for <b>Personal Data</b></p>");
//content.set("content", contentHTML);
}
EDIT 1
This is may be one possible solution.
#Richard had suggested dialog.resize(), which I did try to put it after the html.set code but it would not work.
What I have noticed is that the html.set takes some time to execute and the dialog.resize() does not work because it is
called before the completion of the html.set call.
html.set also complicates the issue as it does not provide any handle (promise object) to let us know when it has finished execution.
so the below solution uses a setTimeout call to delay the execution of the dialog.resize(). Hence would advice to put the value of delay time depending upon some actual UI testing.
Modified code.
handle: function(error, ioargs){
var node = dom.byId(formID);
html.set(node, contentHTML, {parseContent: true});
var dialog = registry.bId("formDialog");
setTimeout( function(){
dialog.show();
dialog.resize();
},2000) // time delay of 2 seconds
}