I'm currently upgrading our app version of angular js from 1.0.8 to the latest stable release and running into an interesting issue.
We have a set of checkboxes used to filter information, in general the options are:
All
Filter 1
Filter 2
Filter N
The desire is to have the 'All' checkbox when clicked, remain checked. It will do additional logic to determine if the other checkboxes are checked and toggle their checked states off if they are. I was able to accomplish this with 1.0.8, but not with the latest version of angular.
At its most basic of setup, we just have a single checkbox, with a ng-model value set to some boolean scope variable and a ng-click defined to some function.
I have a plunker demo where one can toggle between 1.0.8 and 1.2.18 to illustrate the change, here: http://plnkr.co/edit/WaTj6e33x4SZ2NkKStsC?p=info
HTML:
<body ng-app='example' ng-controller="mainCtrl">
<h1>Hello Plunker!</h1>
<button type="button" ng-click="toggleCb(true)">All Checked</button>
<button type="button" ng-click="toggleCb(false)">All Unchecked</button>
<div>
<input type="checkbox" ng-model="allCheckbox" ng-click="toggleCb(true)" id="all"/>
<label for="all">All</label>
</div>
<div>Imagine more checkbox options</div>
<div>
<p>Value for checkbox is: {{ allCheckbox }}</p>
</div>
</body>
JS:
angular.module('example', [])
.controller('mainCtrl', function($scope) {
$scope.allCheckbox = true;
$scope.toggleCb = function(value) {
console.log('called with value', value);
$scope.allCheckbox = value;
}
});
So this basic example setup has a couple of buttons which when clicked will change the boolean value of the scope variable. That is to illustrate that setting the scope variable is being respected by the input. The input is click-bound as well to the same function and when using 1.0.8 it keeps the checkbox checked.
Has anyone had a need to do this with running the latest version of angular, and if so how have you overcome this issue?
Related
I have observed a very peculiar behavior of <s:checkbox> rendering along with Bootstrap 3 and AngularJS.
I have these two <s:checkbox> in my page, wrapped by some elements of Bootstrap 3 styles:
<div class="col-md-1">
<div class="form-group">
<div class="form-other">
<label for="activaCheck"><s:text name="actividad.busqueda.activa"/></label>
<s:checkbox class="form-control" id="activaCheck" name="activaCheck" ng-model="formData.activaCheck" value="true"></s:checkbox>
<s:checkbox class="form-control" id="activaCheck2" name="activaCheck2" value="true"></s:checkbox>
</div>
</div>
</div>
As you can see, the only difference between them, is that the first has attribute ng-model = "xxx", while the second doesn't.
And, in my page, they are rendered differently, although they both are supposed to be pre-selected, because I set value="true". And when we inspect in FF, we can see the first <s:checkbox> has checked="checked", but is not rendered. I have tested in Chrome and FF, same.
I have also tested with <input type="checkbox" /> with ng-model set and checked="checked", the same: not checked when rendered in page.
So I am thinking about AngularJS is taking over part of rendering job which Struts 2 is responsible of, at least in this case. I want some explanation from developers of AngularJS, or this is the expected result?
I got the problem with unchecked checkbox. Because it has ng-model attribute the input control is bound to Angular's $scope. And if the scope doesn't define the property value for the above named checkbox it's not checked. Assumed that AngularJS modifies DOM as soon as it initializes.
I have created plnkr to demonstrate it.
You are right AngularJS starts working after document is loaded. At this time Struts has already done its work and returned html document to the browser. Now Angular continues to prepare the page to work only on one page. Both complement each other, but if Struts use to render
<input type="checkbox" ng-model="checkboxModel.value1" checked="checked">
Angular removes the checked state, because the value is commented
angular.module('checkboxExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.checkboxModel = {
//value1 : true,
value2 : 'YES'
};
}]);
I have a problem with the model binding of my bootstrap datetimepicker, that doesn't update after selecting a date in the dialog.
When I directly edit the input field the binding gets triggered, but selecting doesn't update the binding.
I have a small
Plunker example
<body ng-controller="MainCtrl">
<p>Hello {{name}}! {{datum}}</p>
<div class="input-group">
<span class="input-group-addon add-on"><span class="fa fa-calendar"></span></span>
<input type="text" class="form-control datetimepicker" ng-model="datum"/>
</div>
</body>
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.datum = new Date();
if($(".datetimepicker").length > 0){
$('.datetimepicker').datetimepicker({
format: 'YYYY-MM-DD HH:mm'
});
}
});
Plain bootstrap doesn't work with angular. You will need/should use a bootstrap-angular binding library instead of the bootstrap scripts. You still use the bootstrap css/sass/less etc. Angularstrap is one such library and it doesn't depend on jquery.
When you use angularstraps datepicker it will update your binding.
The (foul, hideous, messy) alternative is to bind an event handler to the bootstrap datepicker change event and from within there call $scope.digest() and/or trigger a changed event manually on the textfield element.
I cannot seem to get the correct way to set the 'checked' attribute in ons-switch. This is so that I can setup user configurations page with pre-checked select boxes.
The Docs:
This is a checked switch but how do I set this using a variable in an angular controller?
For example, if ons-switch has a syntax like
I could have done:
I cannot seem to set attribute "checked" with no value in angular, as needed in the docs. I'm also unable to access the variable since it is part of an array of configurations.
Code Example:
controller:
var categInfo = [{Interest:'Classic', isChecked:true}, {Interest:'New', isChecked:false}];
html:
<ons-list-item ng-repeat="interest in categInfo" >
<span style="color: #666">{{interest.Interest}}</span>
<ons-switch modifier="list-item" var="{{interest.Interest}}" checked="{{interest.isChecked}}"></ons-switch>
</ons-list-item>
So what I want is that the html should show buttons that are checked/unchecked depending on interest.isChecked is true or false.
First of all, you need to bind the switch with ng-model, this will allow you to manage the ons-switch behavior directly from the controller. Setting the variable true or false, inside the controller, will automatically change the value of the state of the switch, same thing if you change the state from the switch (AngularJS binding).
If you want to check the status of the switch, you need to check the model value.
Here is a CodePen example. and the relative code.
HTML
<div ng-controller="MyController">
<ons-switch ng-model="switch"></ons-switch>
<ons-button ng-click="changeSwitch()">Change switch status</ons-button>
</div>
Controller
ons.bootstrap()
.controller('MyController', function ($scope) {
$scope.changeSwitch = function() {
$scope.switch = !$scope.switch;
if($scope.switch)
alert('checked');
else
alert('unchecked');
};
});
EDIT: SWITCH ARRAY EXAMPLE
Due to an Onsen UI bug about the initialization of the ons-switch element, I suggest you to use the following code to implement your switch.
<label class="switch">
<input type="checkbox" class="switch__input" checked>
<div class="switch__toggle"></div>
</label>
The appearance will be the same as the ons-switch element. This bug will be fixed in Onsen UI 1.4 release, so you can start using again the switch element after its release.
For what concerns the behavior of an array of switches, it's analog of the single switch. You still need to use 'ng-model' to bind the status of the switch. You are using ng-repeat to display the switch elements so, by using ng-model="item.isChecked", every element will be binded with the relative isChecked value inside the array. Here you can find a working CodePen example, and this is the relative code:
HTML
<div ng-controller="MyController">
<h2>What I am trying</h2>
<div ng-repeat="item in categInfo">
<div>This button should be {{item.isChecked}}</div>
<label class="switch">
<input ng-model="item.isChecked" type="checkbox" class="switch__input" checked>
<div class="switch__toggle"></div>
</label>
</div>
</div>
Controller
ons.bootstrap()
.controller('MyController', function ($scope, $timeout) {
//Need to go through the array and set as checked or not
$scope.categInfo = [{Interest:'Classic', isChecked:true}, {Interest:'New', isChecked:false}];
});
In my Angular app, I want a certain div to appear if a variable is true, and to disappear if it is false.
However, it is not working. See my Fiddle
Can anyone help me understand why?
HTML
<div ng-controller="MyCtrl">
<div id="newProjectButton" class="project" ng-click="newProjectActive()" ng-if="!creatingNew">
<h1> + </h1>
<h3> New Project </h3>
</div>
<div id="newProjectActive" class="project" ng-if="creatingNew">
<form>
<input name="name" ng-model="newProjectName" type="text"></input>
<button ng-click="newProject()" type="submit" class='btn btn-primary'>Save</button>
</form>
</div>
</div>
JS
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.name = 'Superhero';
$scope.creatingNew = false;
$scope.newProjectActive = function () {
$scope.creatingNew = true;
}
$scope.newProject = function () {
alert($scope.newProjectName);
}
}
your angular version is 1.0.1. directive ng-if is not in this version of angular.
its introduce in angular 1.1.5
check this article
and
check it in angular under Directives topic change log
AngularJS first added the ngIf directive in 1.1.5
please update the angular version
here is the Demo
your controller should be like
myApp.controller("MyCtrl" , function($scope) {
because the global controllers are not supported by default in angular 1.3... check this one
First, when I looked at your fiddle, there was older version of your example there,
Second, which actually may be a reason, is in that example you were using angular in version 1.0.1, and i believe that version didn't implement ng-if. Updating to latest version will fix your problem
I am trying to generate an editable list using ng-repeat. I want to remind the user to update any edits before moving on, so I am using ng-form to create "nested" forms on the fly because the documentation says I can then use validation on these dynamically created inputs.
While that seems to work within the HTML, I don't see how to access those dynamically created forms and related validation fields in the controller. Specifically, when the user changes the input I use the form $dirty property to bring up a button to tell the user to commit the changes. So far, so good. However, once the changes are committed I want to $setPristine() on the field to indicate that the changes have been set. There may be other ways of ensuring that changes are committed on each input before I allow the main form committed, but this was the best I could come up with.
Unfortunately, even though the documentation says that if I name the ng-form it will be propagated to the $scope object, I can't find a way to access it. $scope.dynamic_form is undefined.
Here is a plunker showing what I mean:
plnk
Thanks!
[EDIT] Just to add to the issue, what does work for this specific example is to add to the ng-click on the dynamically created input:
ng-click="namesForm.name.$setPristine();clean()"
But I still don't have access to the dynamically created form in the controller. I would like, for example, to add a watcher to the namesForm.name.$pristine so that I can set the mainForm.$setValidity(false) whenever the sub-form is $dirty to prevent the user from submitting the main form until all sub-form changes have been committed.
So in a nutshell, the issue is how to access in a parent controller the validation values of a dynamically created nested ngForm?
Updated 2015-01-17:
As pointed out by Leblanc Meneses in the comments Angular 1.3 now supports interpolation with form, ngForm and input directives.
This means that using expressions to name your elements:
<div ng-form="namesForm_{{$index}}" ng-repeat="name in names">
<input type="text"
name="input_{{$index}}_0"></input>
<!-- ... -->
</div>
will work as expected:
$scope['namesForm_0']
$scope.namesForm_1
// Access nested form elements:
$scope.namesForm_1.input_1_0
...
Original answer for Angular <= 1.2:
Working with forms and the ngFormController can get tricky pretty quickly.
You need to be aware that you can dynamically add form elements and inputs but they can't be dynamically named - interpolation does not work in the ngForm or name directives.
For example, if you tried to name your nested forms dynamically like this:
<div ng-form="namesForm_{{$index}}" ng-repeat="name in names">
<!-- ... -->
</div>
Instead of making all the nested forms available on the scope like this: scope['namesForm_0'] you would only have access to the single (last) form with the literal name scope['namesForm_{{$index}}'].
In your situation you need to create a custom directive that will be added along with ngFormto handle setting $pristine$ and $invalid for that form instance.
JavaScript:
This directive will watch the $dirty state of its form to set the $validity to prevent submission when dirty and handle setting the $pristine state when the 'clean' button is pressed.
app.directive('formCleaner', function(){
return {
scope: true,
require: '^form',
link: function(scope, element, attr){
scope.clean = function () {
scope.namesForm.$setPristine();
};
scope.$watch('namesForm.$dirty', function(isDirty){
scope.namesForm.$setValidity('name', !isDirty);
});
}
};
});
HTML:
Then the only change to your HTML is to add the formCleaner directive.
So change your original HTML from this:
<body ng-controller="MainCtrl">
<form name="mainForm" submit="submit()">
<h3>My Editable List</h3>
<div ng-form="namesForm"
ng-repeat="name in names">
<!-- ... -->
</div>
<button class="btn btn-default" type="submit">Submit</button>
</form>
</body>
to this, by adding form-cleaner next to ng-form:
<body ng-controller="MainCtrl">
<form name="mainForm" submit="submit()">
<h3>My Editable List</h3>
<!-- Add the `form-cleaner` directive to the element with `ng-form` -->
<div form-cleaner
ng-form="namesForm"
ng-repeat="name in names">
<!-- ... -->
</div>
<button class="btn btn-default" type="submit">Submit</button>
</form>
</body>
Here is an updated Plunker showing the new behaviour: http://plnkr.co/edit/Lxem5HJXe0UCvslqbJr3?p=preview