Why is ng-disabled not working on button? - javascript

My Plunkr: http://plnkr.co/edit/4MkenJPczFbxy5aoillL?p=preview
<body ng-controller="mainCtrl as main">
<h1>Hello Plunker!</h1>
<p>Button should not be disabled:</p>
<div ng-init="main.btnDisabled = false">
<button ng-model="main.my_button"
ng-class="{ 'btn-success' : !tc.switching, 'btn-disabled' : tc.switching }"
disabled="main.btnDisabled"
type="button"
class="btn btn-info btn-sm switch-btn">My Button</button>
</div>
Angular
angular.module('app').controller('mainCtrl', function($scope) {
vm = this;
vm.btnDisabled = false;
});
I found this answer here, but it didn't work in my example.

The button is disabled because there is disabled attribute. This is enough for browser to know that element must be inactive. The value for disabled attribute doesn't matter, it can be anything.
This is exactly the reason why Angular provides ngDisabled directive, which adds disabled attibute when expression evaluates to true, and removes when it's false.
In your case you should use
<button ng-model="main.my_button"
ng-class="{ 'btn-success' : !tc.switching, 'btn-disabled' : tc.switching }"
ng-disabled="main.btnDisabled"
type="button"
class="btn btn-info btn-sm switch-btn">My Button</button>

There are a few problems that I see here.
First, change disabled to ng-disabled.
Second, when you click the button nothing will change/happen. Instead of putting that functionality into your ng-class, use something like ng-click to change the state.
This isn't contributing to your problem but make sure that you include $scope before passing it into your controller function.
Speaking of $scope, the plunker would be a bit easier to read if you put something on the scope instead of using a controller alias. No problem with that, it just might help you and other people debug your code.

Related

more eloquent way to use conditional on buttons for angularjs

right now i'm using an ng-if to determine which button to show my users based on a condition. It just checks if my $scope.method matches the file variable.
<a class="btn btn-default" ng-if="method != file" ng-click="change(file)">Upload</a>
<a class="btn btn-primary" ng-if="method === file" ng-click="change(file)">Upload</a>
however im wondering if there is a more eloquent way to do this by using ng-class or any other angular directives...
Yes, you are correct. ng-class is what you are wanting.
<a class="btn" ng-class="{'btn-default': method != file, 'btn-primary': method === file}"
ng-if="method != file" ng-click="change(file)">Upload</a>
Here is a fantastic article I always reference when I'm needing to use ng-class.
You shall use ng-class to acheive that and add conditionnal class
https://docs.angularjs.org/api/ng/directive/ngClass

Disable angular button using ng-disable using $scope var in controller

I have two buttons in my view and I want to disable the Summary button until my view is loaded.
<div class="btn-group" ng-init="showData = 1">
<button ng-model="showData" type="button" ng-class='{"btn btn-primary": showData == 1, "btn btn-white": showData != 1}' ng-click="showData = 1">Headline</button>
<button ng-model="showData" type="button" ng-class='{"btn btn-primary":showData == 2, "btn btn-white": showData != 2}' ng-click="showData = 2; summaryCall()">Summary</button>
</div>
I have a variable $scope.loadMainView = false, this change to true when the response of a Web service is ok, so I want want to disable my button until that variable change to true, but I dont know how to
achive that. I was thinking on ng-init for a $scope variable to be initialized as false and then asing that to an ng-disable or something like that but I'm not sure, Im new in angular and maybe my
approach is not at all correct.
Some help will be great.
Use ng-disabled
<button ng-disabled="!loadMainView" type="button"></button>
This directive sets the disabled attribute on the element if the expression inside ngDisabled evaluates to truthy.
https://docs.angularjs.org/api/ng/directive/ngDisabled
using the ngDisabled directive is the right way to go
<button ng-disabled="!loadMainView" type="button"></button>

Knockout enable binding not working

I can't get the enable binding to work in Knockout JS. With the enabled property set to false, the button is not disabled and I can still click it.
see fiddle
<a class="btn btn-xl btn-primary"
href="#"
role="button"
data-bind="enable: enabled, click: clicked, visible: isVisible">
<i class="icon-only icon-ok bigger-130"></i>
</a>
var ViewModel = function(){
var self = this;
self.enabled = ko.observable(false);
self.isVisible = ko.observable(true);
self.clicked = function(){
alert('You clicked the button');
};
};
$(function(){
var model = new ViewModel();
ko.applyBindings(model);
})
Enable binding does not work with anything you want.
This is useful with form elements like input, select, and textarea
It also works with buttons. Like in my example http://jsfiddle.net/5CbnH/1/
But it does not work with your link. You are using twitter bootstrap and they enable/disable their "buttons" with css classes. So you have to use css binding like this:
data-bind="css: { yourClass: enabled }"
Check what class is responsible in bootstrap for showing your "button" and modify your code accordingly with css binding.
Right:
✅ enable✅ disable
Wrong:
❌ enabled❌ disabled
Make sure you use disable instead of disabled and enable instead of enabled.
<input type="text" data-bind="value: foo, enable: isEditing"/> YES!!
<input type="text" data-bind="value: foo, enabled: isEditing"/> NO!
Easy mistake to make :-)
For people who might find this in a search:
I had a problem getting the enable binding to work as well. My problem was trying to use a complex expression without referencing the observables like functions:
<input type="button" data-bind="enable:AreAllStepsVerified && IsFormEnabled, click:SubmitViewModel"/>
Should have been:
<input type="button" data-bind="enable:AreAllStepsVerified() && IsFormEnabled(), click:SubmitViewModel"/>
See: https://stackoverflow.com/a/15307588/4230970
What Salvador said in his answer.
You must understand that the enabled and disabled binding in knockout work by putting a disabled attribute on the target DOM element. Now if you look at the HTML documentation you'd notice that not all HTML element support this attribute.
Actually only form elements (e.g. <button>) do. <a> does not.
I got it to work by changing the anchor tag to a button, not really sure why this makes it work, but it works nonetheless.
Updated fiddle.
<button class="btn btn-xl btn-primary"
role="button"
data-bind="enable: enabled, click: clicked, visible: isVisible">
<i class="icon-only icon-ok bigger-130"></i>
</button>

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

How to get element in angularjs analog of get by id in javascript?

Lets say I have button:
<button type="submit"
value="Send"
id="btnStoreToDB"
class="btn btn-primary start"
ng-click="storeDB()"
ng-disabled="!storeDB_button_state"
>
<i class="icon-white icon-share-alt"></i>
<span>Store to DB</span>
</button>
In JS to get this button I just do var btn = $('#btnStoreToDB'); and now can play with this button. So I can take it by id or class.
But how can I get this element with angularjs?
I want to add spinner to button during loading like showed here (Fiddle).
Since all my project I started to use angulajs only I try to do it wisely and do not like how do I know.
I thought to add: ng-model="btnStoreToDB" and use this:
if($scope.btnStoreToDB){
var spinner = new Spinner().spin();
$scope.btnStoreToDB.appendChild(spinner.el);
}
but $scope.btnStartUpload is undefined. Suppose there is other way to fetch this button.
Please, help
This is a good question, and a very common situation.
The Angular way of doing this is to use ng-show, ng-hide or ng-class on or within your button.
One idea might be to use ng-class like so:
<button ng-class="{ useSpinner: btnStoreToDB }">...</button>
This will add the class useSpinner to the button when btnStoreToDB is true, and remove the class when btnStoreToDB is false.
Another way would be to use ng-show like this:
<button ...>
<i class="icon-white icon-share-alt"></i>
<span>Store to DB</span>
<div class="spinner" ng-show="btnStoreToDb">...</div>
</button>
Now your view and controller are separated, and you are doing things the Angular Way.
angular.element("#btnStoreToDB");
But here is more "angular" way
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.loading = false;
$scope.send = function(){
$scope.loading = true;
setTimeout(function(){
alert('finished');
$scope.$apply($scope.loading = false);
}, 2000);
};
});
HTML
<button ng-click="send()">
<img ng-show="loading" src="http://farmville-2.com/wp-content/plugins/farmvilleajpl/img/loadingbig.gif" width='10'>
Send!
</button>
p.s. Image from cutom source
p.p.s personally I usually use Twitter Bootstrap and there is a way to show spinners easier. like <i class="icon-spin icon-spinner" ng-show="loading"></i>
See on Plunker

Categories