HTML div not being updated - angularjs

Not an angular expert. I'm developing a python-flask application with angularjs in the front end. I make a http call to python that returns an object. I then check if the value of a key is 'dev' or 'prd' and then use ng-show to display the corresponding divs. My problem is that the div does not update correctly every time I click on the button to retrieve the data from python.
angular.forEach(response.data,function (value,key) {
if (value == 'dev') {
$scope.showval="True";
//$scope.showprd="False";
} else if (value == 'prd') {
$scope.showprd="True";
//$scope.showval="False";
}
});
I tried $scope.$apply(), $timeout() and also tried to set the ng-show variable to false but none worked. $scope.$apply gave me an error that the digest cycle is in progress. So,i think the divs are being processed by angular. Need help to determine what's wrong. Thanks for your time.

You could set you showval and showprd as false initially and in a certain condition your can assign it as true like following:
angular.forEach(response.data, function(value, key) {
if (value == 'dev') {
$scope.showval = true;
$scope.showprd = false;
} else if (value == 'prd') {
$scope.showprd = true;
$scope.showval = false;
}
});
And i am supposing your html like
<div ng-show="showval && !showprd">Dev</div>
<div ng-show="!showval && showprd">Prod</div>

Related

Can AngularJs $scope variables be conditionally bounded to each other?

So here my problem
I am trying to make form toggles with a global toggle to basically check and uncheck all of them using ng-model.
$scope.globalToggle =
{
"toggle": true
}
$scope.Toggles =
{
"toggle1": true,
"toggle2": true,
"toggle3": true,
}
This is what Im trying to achieve in an efficient manner:
If global toggle is checked set all to true or false.
If any toggle is un-checked set global to false.
If any toggle is checked and the other two toggles are also checked
then set global to true.
Currently I have managed to use a long winded out process of scope functions and adding ng-click to each checkbox element to achieve this, but I was hoping there is some better way to achieve my goal where I don't have to use ng-click on my elements. Is it possible to have some binding between these two variables that can change their value based on the conditions when they are changed from the DOM using ng-model?
Here you have a snipped which does exactly what you want :)
you just have to put ng-click directive on your toggles and write your algorithm
basicaly that:
globalToggleClicked = function() {
for (var toggle in $scope.Toggles) {
if ($scope.globalToggle.toggle == true)
$scope.Toggles[toggle] = true;
else
$scope.Toggles[toggle] = false;
}
};
toggleClicked = function(toggle){
if (toggle == false)
$scope.globalToggle.toggle = false;
else if (toggle == true) {
var nbChecked = 0;
for (var t in $scope.Toggles) {
if ($scope.Toggles[t] == true)
nbChecked++;
}
if (Object.keys($scope.Toggles).length == nbChecked)
$scope.globalToggle.toggle = true;
}
};
multi toggle management
I know it's with ng-click, but you don't really have other ways to do that

Toggling between $pristine and $dirty in AngularJS

I want to detect when a user has entered values into any form field by using the $dirty property and setting a flag accordingly. Not surprisingly, this works:
$scope.$watch('formDetails.$dirty', function() {
USR.userInputRecorded = true;
});
But I'd also like to detect when/if the user has emptied all fields and effectively restored the form to its original empty state. The snippet below does not work and I'm not sure why. Is there a way to watch for when the form changes back to "not dirty"?
$scope.$watch('formDetails.$pristine', function() {
USR.userInputRecorded = false;
});
Thanks.
Try this:
$scope.$watch('formDetails.$dirty', function(value) {
if (value === '') {
// field has been emptied;
your.form.$setPristine(true);
} else {
USR.userInputRecorded = true;
}
});

angular filter on object works but causes Infinite $digest Loop

I gave an object as followed
{
key1: [{...}, {...} ....],
key2: [{...}, {...} ....],
.........so on .....
}
I have an ng-repeat ng-repeat="(key, values) in data" and then inside that ng-repeat="val in values"
I want to set up an filter based on some property of objects stored in the array. I have set up below filter
.filter('objFilter', function () {
return function (input, search,field) {
if (!input || !search || !field)
return input;
var expected = ('' + search).toLowerCase();
var result = {};
angular.forEach(input, function (value, key) {
result[key] = [];
if(value && value.length !== undefined){
for(var i=0; i<value.length;i++){
var ip = value[i];
var actual = ('' + ip[field]).toLowerCase();
if (actual.indexOf(expected) !== -1) {
result[key].push(value[i]);
}
}
}
});
console.log(result);
return result;
};
The filter seems to work fine when I use ng-repeat="(date, values) in data| objFilter:search:'url'" but for some reason it is called too many times and causes Infinite $digest Loop.
Any solutions??
Edit:
I have created below plunker to show the issue. The filter works but look in the console for the errors. http://plnkr.co/edit/BXyi75kXT5gkK4E3F5PI
Your filter causes an infinite $digest loop because it always returns a new object instance. With the same parameters it returns a new object instance (it doesn't matter if the data inside the object is the same as before).
Something causes a second digest phase. I'm guessing it's the nested ng-repeat. Angular calls filters on every digest phase and because you filter returns a new value it causes the framework to reevaluate the whole outer ng-repeat block which causes the same on the inner ng-repeat block.
Option 1 - modify the filter
One fix you can do is to "stabilize" the filter. If it's called 2 times in a row with the same value it should return the same result.
Replace your filter with the following code:
app.filter('objFilter', function () {
var lastSearch = null;
var lastField = null;
var lastResult = null;
return function (input, search, field) {
if (!input || !search || !field) {
return input;
}
if (search == lastSearch && field == lastField) {
return lastResult;
}
var expected = ('' + search).toLowerCase();
var result = {};
angular.forEach(input, function (value, key) {
result[key] = [];
if(value && value.length !== undefined){
for(var i=0; i<value.length;i++){
var ip = value[i];
var actual = ('' + ip[field]).toLowerCase();
if (actual.indexOf(expected) !== -1) {
//if(result[key]){
result[key].push(value[i]);
//}else{
// result[key] = [value[i]];
//}
}
}
}
});
// Cache params and last result
lastSearch = search;
lastField = field;
lastResult = result;
return result;
};
});
This code will work but it's bad and prone to errors. It might also cause memory leaks by keeping the last provided arguments in memory.
Option 2 - move the filter on model change
Better approach will be to remember the updated filtered data on model change. Keep you JavaScript as is. Change only the html:
<body ng-controller="MainCtrl">
<div ng-if="data">
Search:
<input type="text"
ng-model="search"
ng-init="filteredData = (data | objFilter:search:'url')"
ng-change="filteredData = (data | objFilter:search:'url')">
<div ng-repeat="(date, values) in filteredData">
<div style="margin-top:30px;">{{date}}</div>
<hr/>
<div ng-repeat="val in values" class="item">
<div class="h-url">{{val.url}}</div>
</div>
</div>
</div>
</body>
First we add a wrapper ng-if with a requirement that data must have a value. This will ensure that our ng-init will have "data" in order to set the initial filteredData value.
We also change the outer ng-repeat to use filteredData instead of data. Then we update filtered data on the model change with the ng-change directive.
ng-init will fire once after data value is set
ng-change will be executed only when the user changes the input value
Now, no matter how many consecutive $digest phases you'll have, the filter won't fire again. It's attached on initialization (ng-init) and on user interaction (ng-change).
Notes
Filters fire on every digest phase. As a general rule try avoiding attaching complex filters directly on ng-repeat.
Every user interaction with a field that has ng-model causes a $digest phase
Every call of $timeout causes a $digest phase (by default).
Every time you load something with $http a digest phase will begin.
All those will cause the ng-repeat with attached filter to reevaluate, thus resulting in child scopes creation/destruction and DOM elements manipulations which is heavy. It might not lead to infinite $digest loop but will kill your app performance.

Testing AngularJS with Selenium

I have a SPA application on stack ASP MVC + AngularJS and I'd like to test the UI.
For now I'm trying Selenium with PhantomJS and WebKit drivers.
This is a sample testing page - view with single element. The list items <li> load dynamically from server and are bounded by Angular.
<div id="items">
<li>text</li>
<li>text2</li>
</div>
I'm trying to pass a test and there is an error in this line:
_driver.FindElements(By.TagName('li'))
At this point there are no loaded elements and _driver.PageSource doesn't contain elements.
How can I wait for the items to load? Please do not suggest Thread.Sleep()
This will wait for page loads / jquery.ajax (if present) and $http calls, and any accompanying digest/render cycle, throw it in a utility function and wait away.
/* C# Example
var pageLoadWait = new WebDriverWait(WebDriver, TimeSpan.FromSeconds(timeout));
pageLoadWait.Until<bool>(
(driver) =>
{
return (bool)JS.ExecuteScript(
#"*/
try {
if (document.readyState !== 'complete') {
return false; // Page not loaded yet
}
if (window.jQuery) {
if (window.jQuery.active) {
return false;
} else if (window.jQuery.ajax && window.jQuery.ajax.active) {
return false;
}
}
if (window.angular) {
if (!window.qa) {
// Used to track the render cycle finish after loading is complete
window.qa = {
doneRendering: false
};
}
// Get the angular injector for this app (change element if necessary)
var injector = window.angular.element('body').injector();
// Store providers to use for these checks
var $rootScope = injector.get('$rootScope');
var $http = injector.get('$http');
var $timeout = injector.get('$timeout');
// Check if digest
if ($rootScope.$$phase === '$apply' || $rootScope.$$phase === '$digest' || $http.pendingRequests.length !== 0) {
window.qa.doneRendering = false;
return false; // Angular digesting or loading data
}
if (!window.qa.doneRendering) {
// Set timeout to mark angular rendering as finished
$timeout(function() {
window.qa.doneRendering = true;
}, 0);
return false;
}
}
return true;
} catch (ex) {
return false;
}
/*");
});*/
Create a new class that lets you figure out whether your website using AngularJS has finished making AJAX calls, as follows:
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
public class AdditionalConditions {
public static ExpectedCondition<Boolean> angularHasFinishedProcessing() {
return new ExpectedCondition<Boolean>() {
#Override
public Boolean apply(WebDriver driver) {
return Boolean.valueOf(((JavascriptExecutor) driver).executeScript("return (window.angular !== undefined) && (angular.element(document).injector() !== undefined) && (angular.element(document).injector().get('$http').pendingRequests.length === 0)").toString());
}
};
}
}
You can use it anywhere in the your code by using the following code:
WebDriverWait wait = new WebDriverWait(getDriver(), 15, 100);
wait.until(AdditionalConditions.angularHasFinishedProcessing()));
We have had a similar issue where our in house framework is being used to test multiple sites, some of these are using JQuery and some are using AngularJS (and 1 even has a mixture!). Our framework is written in C# so it was important that any JScript being executed was done in minimal chunks (for debugging purposes). It actually took a lot of the above answers and mashed them together (so credit where credit is due #npjohns). Below is an explanation of what we did:
The following returns a true / false if the HTML DOM has loaded:
public bool DomHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
{
var hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");
while (hasThePageLoaded == null || ((string)hasThePageLoaded != "complete" && timeout > 0))
{
Thread.Sleep(100);
timeout--;
hasThePageLoaded = jsExecutor.ExecuteScript("return document.readyState");
if (timeout != 0) continue;
Console.WriteLine("The page has not loaded successfully in the time provided.");
return false;
}
return true;
}
Then we check whether JQuery is being used:
public bool IsJqueryBeingUsed(IJavaScriptExecutor jsExecutor)
{
var isTheSiteUsingJQuery = jsExecutor.ExecuteScript("return window.jQuery != undefined");
return (bool)isTheSiteUsingJQuery;
}
If JQuery is being used we then check that it's loaded:
public bool JqueryHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
{
var hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");
while (hasTheJQueryLoaded == null || (!(bool) hasTheJQueryLoaded && timeout > 0))
{
Thread.Sleep(100);
timeout--;
hasTheJQueryLoaded = jsExecutor.ExecuteScript("jQuery.active === 0");
if (timeout != 0) continue;
Console.WriteLine(
"JQuery is being used by the site but has failed to successfully load.");
return false;
}
return (bool) hasTheJQueryLoaded;
}
We then do the same for AngularJS:
public bool AngularIsBeingUsed(IJavaScriptExecutor jsExecutor)
{
string UsingAngular = #"if (window.angular){
return true;
}";
var isTheSiteUsingAngular = jsExecutor.ExecuteScript(UsingAngular);
return (bool) isTheSiteUsingAngular;
}
If it is being used then we check that it has loaded:
public bool AngularHasLoaded(IJavaScriptExecutor jsExecutor, int timeout = 5)
{
string HasAngularLoaded =
#"return (window.angular !== undefined) && (angular.element(document.body).injector() !== undefined) && (angular.element(document.body).injector().get('$http').pendingRequests.length === 0)";
var hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);
while (hasTheAngularLoaded == null || (!(bool)hasTheAngularLoaded && timeout > 0))
{
Thread.Sleep(100);
timeout--;
hasTheAngularLoaded = jsExecutor.ExecuteScript(HasAngularLoaded);
if (timeout != 0) continue;
Console.WriteLine(
"Angular is being used by the site but has failed to successfully load.");
return false;
}
return (bool)hasTheAngularLoaded;
}
After we check that the DOM has successfully loaded, you can then use these bool values to do custom waits:
var jquery = !IsJqueryBeingUsed(javascript) || wait.Until(x => JQueryHasLoaded(javascript));
var angular = !AngularIsBeingUsed(javascript) || wait.Until(x => AngularHasLoaded(javascript));
If you're using AngularJS then using Protractor is a good idea.
If you use protractor you can use it's waitForAngular() method which will wait for http requests to complete. It's still good practise to wait for elements to be displayed before acting on them, depending on your language and implementation it might look this in a synchronous language
WebDriverWait wait = new WebDriverWait(webDriver, timeoutInSeconds);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id<locator>));
Or in JS you can use wait method which executes a function until it returns true
browser.wait(function () {
return browser.driver.isElementPresent(elementToFind);
});
You may just mine protractor for useful code snippets. This function blocks until Angular is done rendering the page. It is a variant of Shahzaib Salim's answer, except that he is polling for it and I am setting a callback.
def wait_for_angular(self, selenium):
self.selenium.set_script_timeout(10)
self.selenium.execute_async_script("""
callback = arguments[arguments.length - 1];
angular.element('html').injector().get('$browser').notifyWhenNoOutstandingRequests(callback);""")
Replace 'html' for whatever element is your ng-app.
It comes from https://github.com/angular/protractor/blob/71532f055c720b533fbf9dab2b3100b657966da6/lib/clientsidescripts.js#L51
I did the following code and it helped me for the async race condition failures.
$window._docReady = function () {
var phase = $scope.$root.$$phase;
return $http.pendingRequests.length === 0 && phase !== '$apply' && phase !== '$digest';
}
Now in selenium PageObject model, you can wait for
Object result = ((RemoteWebDriver) driver).executeScript("return _docReady();");
return result == null ? false : (Boolean) result;
If your web app is indeed created with Angular as you say, the best way to do end-to-end testing is with Protractor.
Internally, Protractor uses its own waitForAngular method, to ensure Protractor waits automatically until Angular has finished modifying the DOM.
Thus, in the normal case, you would never need to write an explicit wait in your test cases: Protractor does that for you.
You can look at the Angular Phonecat tutorial to learn how to set up Protractor.
If you want to use Protractor seriously, you will want to adopt pageobjects. If you want an example of that have a look at my page object test suite for the Angular Phonecat.
With Protractor you write your tests in Javascript (Protractor is indeed based on Node), and not in C# -- but in return Protractor handles all waiting for you.
For my particular problem with the HTML page containing iframes and developed with AnglularJS the following trick saved me a lot of time:
In the DOM I clearly saw that there is an iframe which wraps all the content.
So following code supposed to work:
driver.switchTo().frame(0);
waitUntilVisibleByXPath("//h2[contains(text(), 'Creative chooser')]");
But it was not working and told me something like "Cannot switch to frame. Window was closed".
Then I modified the code to:
driver.switchTo().defaultContent();
driver.switchTo().frame(0);
waitUntilVisibleByXPath("//h2[contains(text(), 'Creative chooser')]");
After this everything went smoothly.
So evidently Angular was mangling something with iframes and just after loading the page when you expect that driver is focused on default content it was focused by some already removed by Angular frame.
Hope this may help some of you.
If you don't want to make the entire switch to Protractor but you do want to wait for Angular I recommend using Paul Hammants ngWebDriver (Java). It's based on protractor but you don't have to make the switch.
I fixed the problem by writing an actions class in which I waited for Angular (using ngWebDriver's waitForAngularRequestsToFinish()) before carrying out the actions (click, fill, check etc.).
For a code snippet see my answer to this question
Here is an example for how to wait on Angular if you're using WebDriverJS. Originally I thought you had to create a custom condition, but wait accepts any function.
// Wait for Angular to Finish
function angularReady(): any {
return $browser.executeScript("return (window.angular !== undefined) && (angular.element(document).injector() !== undefined) && (angular.element(document).injector().get('$http').pendingRequests.length === 0)")
.then(function(angularIsReady) {
return angularIsReady === true;
});
}
$browser.wait(angularReady, 5000).then(...);
Sadly this doesn't work with PhantomJS because of CSP (content-security-policy) and unsafe-eval. Can't wait for headless Chrome 59 on Windows.
I have implemented usage based on D Sayar's answer
And it might helpful for someone. You just have to copy all boolean functions mention over there in to single class, And then add below PageCallingUtility() method. This method is calling internal dependency.
In your normal usage you need to directly call PageCallingUtility() method.
public void PageCallingUtility()
{
if (DomHasLoaded() == true)
{
if (IsJqueryBeingUsed() == true)
{
JqueryHasLoaded();
}
if (AngularIsBeingUsed() == true)
{
AngularHasLoaded();
}
}
}
Beside eddiec's suggest. If you test an AngularJS app, I strongly suggest you to think about protractor
Protractor will help you solve the waiting matter (sync, async). However, there are some notes
1 - You need to develop your test in javascript
2 - There are some different mechanism in handling flow

ExtJs problem in filtering store

I'm having trouble in store filtering. My filter function is working fine and returning true/false as expected.. but in the end all the records are filtered out!!
The xstore is reference to store of Grid. I have also used the main store variable.. but no luck!! Any help is appriciated.
xstore.filterBy(function(rec){
app_rec = rec.get('APPNAME').toUpperCase(); //Record's value that needs to be checked'
Ext.each(elems,function(el){ //For each record, it checks 7 (dynamic) elements
//var ischecked = Ext.get(Ext.getCmp(el.id).teamName+'cb').dom.checked;
if(Ext.getCmp(el.id).teamName.toUpperCase() == app_rec)
{// If Element's attribute 'teamname' is matched then check if element's chkbox is chked/unched'
var ischecked = Ext.get(Ext.getCmp(el.id).teamName+'cb').dom.checked; //get the checkbox
//alert("app_rec: "+app_rec+"panelTeam: " + Ext.getCmp(el.id).teamName.toUpperCase()+"isChecked: "+ischecked );
if(ischecked) //if isChecked... keep record.. below alert if working as expected
{ alert("return true"+"app_rec: "+app_rec+"panelTeam: " + Ext.getCmp(el.id).teamName.toUpperCase()+"isChecked: "+ischecked);
return true;}
else //Else avoid record
{ //alert("return false");
return false;}
}
});
Thanks,
Tushar Saxena
Ext.each is different from a regular JavaScript for loop in that you can return false within the each call to stop iteration. The Ext.each documentation mentions this:
If the supplied function returns false, iteration stops and this
method returns the current index.
So when you're returning from within the each call, you're not returning true/false to the filterBy function like you expect, but to the each function.
Try keeping a handle on isChecked outside of the each loop, and then return true/false based on what was found inside the each function:
// excluded your other code to highlight area around Ext.each call
var isChecked = false;
Ext.each(elems, function(el){
if(Ext.getCmp(el.id).teamName.toUpperCase() == app_rec) {
ischecked = Ext.get(Ext.getCmp(el.id).teamName+'cb').dom.checked;
// can exit early if isChecked is true
if(isChecked){
return false; // this will exit the Ext.each method
}
}
});
// if this is true, filterBy will include the record
return isChecked;

Resources