angularJs disabling form buttons on submit - angularjs

in my Angular 1.x app I am getting a list of Offers via an api call to my backend.
For each Offer returned in the reply I am creating an ng-form. I then display the forms in a modal and I want to be able to disable each form's submit button when it has been clicked to avoid multiple clicks whilst the form data is posted to the back end.
This seems tricky since the number of Offers is an unknown, therefore I'm not sure how I can initialize a variable for each Offer in order to disable the button.
The task would be far more straight forward if I just had one single form, I colud set:
$scope.disableButton = true
... then use ng-disabled on the button
Thusfar I am displaying my forms as follows:
<div ng-form ng-repeat="i in offers track by $index" name="messageForm[$index]" class="row ng-cloak">
....
<button type="button" ng-click="offerRespond(messageForm[$index])" ng-disabled="!messageForm[$index].$valid || offer.i.disableButtons">Submit</button>
</div>
Then in my controller's offerRespond function:
offer = this;
offer.i.disableButtons = true;
This doesn't work of course but it is as close as I can get.
A hack would be to parse the Offers object before passing it to the frontend but that just seems like a horrible hack.

Actually I almost had the answer in my implementation, I just was referring to my variables in correctly:
ng-disabled="!messageForm[$index].$valid || offer.i.disableButtons"
should have been
ng-disabled="!messageForm[$index].$valid || i.disableButtons"
Thanks to #igor for giving me an idea to test which enabled me to revealed the answer myself.

Related

How to click on a deeply buried button in div classes via protractor. No id

I am trying to click a button that is buried in div classes in the code via protractor.
I am pioneering a protractor project for my work and have reached a point where I no longer know what to do. I have a button that is buried in div classes and is not allowing me to click. I have tried using mouseMove to get over to the coordinates of the button, I have tried using the className of the specific button, etc. The button does not have an id. The id is not the issue as I have tried clicking a different button, equally buried in divs, by it's id. I need to know how to get through the layers of divs in order to click the button because the rest of the tests will be dependent on it.
APPLICATION CODE:
::before
<dashboard-label>
<div class="att-topic-analysis-tabs">
<div class="att-button-group">
<button class="btn btn-default btn-lg att-close-topic ng-scope"
role="presentation" tabindex="-1"
ng-click="removeTopic(currentTopic.id)" translate>
Close Topic
</button>
</div>
</div>
PROTRACTOR TEST:
it('Closes Topic Successfully', function(){
//opens the first available topic
openTopic.click();
//checks that the URL contains 'topics' after 5 seconds
browser.wait(proExpect.urlContains('topics'), 5000);
var closeTopic = element(by.className('att-close-topic'));
//browser.wait(proExpect.elementToBeClickable(closeTopicButton), 5000);
console.log(closeTopic);
closeTopic.click();
browser.wait(proExpect.urlContains('home'), 5000);
});
As you can see, the Close Topic button is kind of buried in div classes and the standard click isn't working. Any info would be greatly appreciated
If the closeTopic locator is finding the element, but failing to click it, check to make sure there's only one matching element in the DOM, and that it's visible. My favorite way to check the DOM is just ctrl-F in Chrome inspector and paste the exact CSS that the test is using (.att-close-topic). And to check that what it's getting is visible, use
console.log(closeTopic.isDisplayed());
This can be a big gotcha in protractor, because it doesn't fail (only warns) when there are multiple matches on the page, and it defaults to the first match rather than the first visible match, which drives me nuts, because it's very rare that you want to do anything with a non-visible element on the page.
This will be partly opinion, but just to add a layer to the conversation...
Sometimes the solution to locating a troublesome element on the page is to go back to the developers and make the page more testable. I've seen testers spend hours or days crafting brilliant workarounds to access a stubborn element, and the end result was a fragile, complicated end-to-end test (and aren't they fragile enough already?).
Sometimes a 5-minute conversation with a developer can result in a quick change in the production code (e.g. add a unique ID) that avoids all that effort and yields a much better result, more stable, more simple. But this requires open conversation between the dev and test team, and a culture that values testing as a primary activity enough to make those testability changes to production code that is otherwise working just fine.
This is what you want to read to help you debug why your test doesn't work.
Also, you might want to start adopting await/async since the control flow will go away in the future.
http://www.protractortest.org/#/debugging
try this
var closebutton=element(by.css("[ng-click="removeTopic(currentTopic.id)"]"),
EC = protractor.ExpectedConditions;
Waits for the element to be clickable.checks for display and enable state of button
browser.wait(EC.elementToBeClickable(closebutton), 10000);
now use : closebutton.click();

use angularjs ui-router to implement a summary page

My web-app is written by AngularJs+ ui-router.
The web-app contains many forms (the number of forms are vary, depending on the application the user is applying). Each form has a ui-route state, so our users can go to each form and fill the information.
Before users submit the application we would like to implement a "summary/review" state(page) that contains all the forms the user filled, so users can review (and print) all the information from one page. Is there any way I can use the same form template (templateUrl) for the summary page?
I was thinking to use ng-include and programmatically(ng-repeat) list out all the selected forms, but it seems doesn't work.
PS: my form template might use different controller..
OK, I figured this out.
For ng-include: we need to use
<ng-include src="'formPath'"></ng-include>
For putting the ng-include in a repeater:
<div ng-repeat="f in vm.thisApp.RequiredForms">
<ng-include src="f.FormPath"></ng-include>
</div>
I hope this helps anyone who needs the answer.

Angularjs dropdownlist and form validation

I have a page with several dropdown lists and some of them may have default entries that are not valid. It's essentially the same problem that was described here:
AngularJs doesn't validate a invalid dropdown selection
However, I think there has to be a reasonable way to accomplish this without having to check everything manually in the controller. I'd like to be able to do something like this:
<select ng-model="thisEntry" ng-options="option for option in goodOptions"></select>
<span class="error" ng-show="goodOptions.indexOf(thisEntry) == -1">Please select a valid entry from the list</span>
that also sets the form invalid so the user can't submit until it's fixed . The code above actually displays the message, but I don't know how to set the form invalid at the same time, or even in an equivalent line of code. This seems like the equivalent of a blank entry on a form that's easily handled without having to resort to javascript code.

AngularJS using ng-if vs ng-show

In my AngularJS I have the following code where I check if there is a currently logged in user or not in order to switch the top app menu text from Login to Logout (login if no user is logged in) and vice versa. When I used ng-show ng-hide the app started to be extremely heavy so I tried switching to ng-if, but then the css effects on the top menu started not to work specifically in the login/ logout tab. So can someone please tell me what is the best approach to handle this situation with example please? Thanks
index.html
<div ng-controller="MenuController">
<li>
<div ng-if="userLevel() == 1">
Login
</div>
<div ng-if="userLevel() == 2">
Logout
</div>
</li>
</ul>
</div>
Controller:
controller('MenuController',
function MenuController($scope, UService){
$scope.userLevel = function(){
var userType = UService.checkULevel(); //This will return either 1, 2,3,4...etc
return userType;
};
});
The difference between ng-show and ng-if is that ng-show applies a display: none to the element when the specified expression is a false value, while the ng-if removes the node from the DOM, basically equivalent to the .empty in jQuery.
An approach you can consider for your element, is rather than using it within a controller, use a directive for the access level, and follow the approach described in this article, which is really flexible and allows you to have different elements in the UI depending on the user level: http://frederiknakstad.com/2013/01/21/authentication-in-single-page-applications-with-angular-js/
Another reason for your application to be slow when you check the user level, could be that every time that is evaluated your application has to perform a check on the server side, slowing the application. An approach for it would be to cache the result of that query, and then use it while the login status doesnt change. At that stage you can invalidate the cache and fetch the user level again, ready to update the UI.
The ng-if directive removes the content from the page and ng-show/ng-hide uses the CSS display property to hide content.
I am pretty sure that no-show is lighter than ng-if and no-show should not make the app too heavy. If it is becoming heavy, I think there could be other causes for it.
If you use ng-if the node is rendered only when the condition is true
In case of ng-show ng-hide the Nodes will be rendered but shown/hidden based on the condition if condition changes the same nodes are shown/hidden
when ever you use ng-if it will render only that code which satisfy the condition.
while ng-show ng-hide will render the code on page but will be hidden with the help of CSS properties.
so better to use ng-if for reducing the line of code to be rendered on page.

Why is my Angular JS ng-show empty list div shows up for a split second while API is being called?

<div ng-show="!activities.length">No items in feed</div>
I use the above code to show a message when a user has no items in their feed. On that same page I have a radio button to show different types of feeds (e.g. just yours, or all your friends, etc.). When the user selects a different option, it makes a post back to my API which takes a second, but while the api is grabbing the data the empty list message displays for a second.
Is there an easy way to resolve this?
Use the ngCloack directive for this:
http://docs.angularjs.org/api/ng.directive:ngCloak
The other option is to use ng-bind as many people recommend it over ng-cloak:
http://docs.angularjs.org/api/ng.directive:ngBind
I personally have had issues where even using ng-cloak there is a slight markup flash.

Resources