Invalid first value in formatter with datapicker field
Similar formatter with text input works fine
You can see an example in the code bellow:
/* global angular */
(function() {
'use strict';
var app = angular.module('formlyExample', ['formly', 'formlyBootstrap', 'ui.bootstrap']);
app.run(function(formlyConfig) {
var attributes = [
'date-disabled',
'custom-class',
'show-weeks',
'starting-day',
'init-date',
'min-mode',
'max-mode',
'format-day',
'format-month',
'format-year',
'format-day-header',
'format-day-title',
'format-month-title',
'year-range',
'shortcut-propagation',
'datepicker-popup',
'show-button-bar',
'current-text',
'clear-text',
'close-text',
'close-on-date-selection',
'datepicker-append-to-body'
];
var bindings = [
'datepicker-mode',
'min-date',
'max-date'
];
var ngModelAttrs = {};
angular.forEach(attributes, function(attr) {
ngModelAttrs[camelize(attr)] = {
attribute: attr
};
});
angular.forEach(bindings, function(binding) {
ngModelAttrs[camelize(binding)] = {
bound: binding
};
});
formlyConfig.setType({
name: 'datepicker',
templateUrl: 'datepicker.html',
wrapper: ['bootstrapLabel', 'bootstrapHasError'],
defaultOptions: {
ngModelAttrs: ngModelAttrs,
templateOptions: {
datepickerOptions: {
format: 'MM.dd.yyyy'
}
}
},
controller: ['$scope',
function($scope) {
$scope.datepicker = {};
$scope.datepicker.opened = false;
$scope.datepicker.open = function($event) {
$scope.datepicker.opened = !$scope.datepicker.opened;
};
}
]
});
function camelize(string) {
string = string.replace(/[\-_\s]+(.)?/g, function(match, chr) {
return chr ? chr.toUpperCase() : '';
});
// Ensure 1st char is always lowercase
return string.replace(/^([A-Z])/, function(match, chr) {
return chr ? chr.toLowerCase() : '';
});
}
});
app.controller('MainCtrl', function MainCtrl(formlyVersion) {
var vm = this;
// funcation assignment
vm.onSubmit = onSubmit;
// variable assignment
vm.author = { // optionally fill in your info below :-)
name: 'Kent C. Dodds',
url: 'https://twitter.com/kentcdodds'
};
vm.exampleTitle = 'UI Bootstrap Datepicker'; // add this
vm.env = {
angularVersion: angular.version.full,
formlyVersion: formlyVersion
};
vm.model = {
year: 2005,
text: 'hello'
};
vm.options = {};
vm.fields = [{
key: 'year',
type: 'datepicker',
templateOptions: {
label: 'Year',
type: 'text',
datepickerPopup: 'dd-MMMM-yyyy',
datepickerOptions: {
format: 'yyyy',
minMode: 'year'
}
},
parsers: [dateToYear],
formatters: [yearToDate]
}, {
key: 'text',
type: 'input',
templateOptions: {
label: 'Text',
},
parsers: [toLowerCase],
formatters: [toUpperCase]
}];
vm.originalFields = angular.copy(vm.fields);
// function definition
function onSubmit() {
vm.options.updateInitialValue();
alert(JSON.stringify(vm.model), null, 2);
}
function yearToDate(value) {
console.log('yearToDate: ' + value);
if (!value) {
return new Date();
}
return (typeof value === 'object') ? value : (new Date()).setFullYear(value);
}
function dateToYear(value) {
console.log('dateToYear: ' + value);
return (typeof value === 'object') ? value.getFullYear() : null;
}
function toUpperCase(value) {
console.log('toUpperCase: ' + value);
return (value || '').toUpperCase();
}
function toLowerCase(value) {
console.log('toLowerCase: ' + value);
return (value || '').toLowerCase();
}
});
})();
body {
margin: 20px
}
.formly-field {
margin-bottom: 16px;
}
<!DOCTYPE html>
<html>
<head>
<!-- Twitter bootstrap -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet">
<!-- apiCheck is used by formly to validate its api -->
<script src="//npmcdn.com/api-check#latest/dist/api-check.js"></script>
<!-- This is the latest version of angular (at the time this template was created) -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<!-- Angular-UI Bootstrap has tabs directive we want -->
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.3.2/ui-bootstrap-tpls.js"></script>
<!-- This is the latest version of formly core. -->
<script src="//npmcdn.com/angular-formly#latest/dist/formly.js"></script>
<!-- This is the latest version of formly bootstrap templates -->
<script src="//npmcdn.com/angular-formly-templates-bootstrap#latest/dist/angular-formly-templates-bootstrap.js"></script>
<title>Angular Formly Example</title>
</head>
<body ng-app="formlyExample" ng-controller="MainCtrl as vm">
<div>
<h1>angular-formly example: {{vm.exampleTitle}}</h1>
<div>
This is a <strong>very</strong> basic example of how to use the
UI Boostrap datepicker
with angular-formly.
</div>
<hr />
<form ng-submit="vm.onSubmit()" name="vm.form" novalidate>
<formly-form model="vm.model" fields="vm.fields" options="vm.options" form="vm.form">
<button type="submit" class="btn btn-primary submit-button" ng-disabled="vm.form.$invalid">Submit</button>
<button type="button" class="btn btn-default" ng-click="vm.options.resetModel()">Reset</button>
</formly-form>
</form>
<hr />
<h2>Model Value</h2>
<pre>{{vm.model | json}}</pre>
<h2>Fields <small>(note, functions are not shown)</small></h2>
<pre>{{vm.originalFields | json}}</pre>
<h2>Form</h2>
<pre>{{vm.form | json}}</pre>
</div>
<div style="margin-top:30px">
<small>
This is an example for the
angular-formly
project made with ♥ by
<strong>
<span ng-if="!vm.author.name || !vm.author.url">
{{vm.author.name || 'anonymous'}}
</span>
<a ng-if="vm.author.url" ng-href="{{::vm.author.url}}">
{{vm.author.name}}
</a>
</strong>
<br />
This example is running angular version "{{vm.env.angularVersion}}" and formly version "{{vm.env.formlyVersion}}"
</small>
</div>
<!-- Put custom templates here -->
<script type="text/ng-template" id="datepicker.html">
<p class="input-group">
<input type="text" id="{{::id}}" name="{{::id}}" ng-model="model[options.key]" class="form-control" ng-click="datepicker.open($event)" uib-datepicker-popup="{{to.datepickerOptions.format}}" is-open="datepicker.opened" datepicker-options="to.datepickerOptions"
/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="datepicker.open($event)" ng-disabled="to.disabled"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</script>
</body>
</html>
Look console logs: first time formatter get EMPTY value, but have to 2005 (as defined in vm.model)
P.S.
In this example I noticed one more bug:
initial value datepicker is not formatted
I got Tue May 17 2016 18:25:03 GMT+0300 (EEST) but have to 2016
I check datapicker same version without formly and it works fine. Example bellow:
angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']);
angular.module('ui.bootstrap.demo').controller('DatepickerPopupDemoCtrl', function($scope) {
$scope.dt = '2005-01-31';
$scope.today = function() {
$scope.dt = new Date();
};
$scope.today();
$scope.clear = function() {
$scope.dt = null;
};
$scope.inlineOptions = {
minDate: new Date(),
showWeeks: true
};
$scope.dateOptions = {
formatYear: 'yy',
startingDay: 1
};
$scope.open1 = function() {
$scope.popup1.opened = true;
};
$scope.open2 = function() {
$scope.popup2.opened = true;
};
$scope.setDate = function(year, month, day) {
$scope.dt = new Date(year, month, day);
};
$scope.popup1 = {
opened: false
};
$scope.popup2 = {
opened: false
};
});
body {
padding: 20px;
}
<!doctype html>
<html ng-app="ui.bootstrap.demo">
<head>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular-animate.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-1.3.2.js"></script>
<script src="example.js"></script>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div ng-controller="DatepickerPopupDemoCtrl">
<pre>Selected date is: <em>{{dt | date:'fullDate' }}</em></pre>
<h4>Popup</h4>
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup="MMM dd, yyyy" ng-model="dt" is-open="popup1.opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open1()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
<div class="col-md-6">
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup ng-model="dt" is-open="popup2.opened" datepicker-options="dateOptions" ng-required="true" close-text="Close" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open2()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
<hr />
<button type="button" class="btn btn-sm btn-info" ng-click="today()">Today</button>
<button type="button" class="btn btn-sm btn-default" ng-click="setDate(2009, 7, 24)">2009-08-24</button>
<button type="button" class="btn btn-sm btn-danger" ng-click="clear()">Clear</button>
</div>
</body>
</html>
I suppose formatters not calling first time when template initializing.
An example of input[type=number] throwing an error if the initial value is a string:
/* global angular */
(function() {
'use strict';
var app = angular.module('formlyExample', ['formly', 'formlyBootstrap', 'ui.bootstrap']);
app.run(function(formlyConfig) {
var attributes = [
'date-disabled',
'custom-class',
'show-weeks',
'starting-day',
'init-date',
'min-mode',
'max-mode',
'format-day',
'format-month',
'format-year',
'format-day-header',
'format-day-title',
'format-month-title',
'year-range',
'shortcut-propagation',
'datepicker-popup',
'show-button-bar',
'current-text',
'clear-text',
'close-text',
'close-on-date-selection',
'datepicker-append-to-body'
];
var bindings = [
'datepicker-mode',
'min-date',
'max-date'
];
var ngModelAttrs = {};
angular.forEach(attributes, function(attr) {
ngModelAttrs[camelize(attr)] = {
attribute: attr
};
});
angular.forEach(bindings, function(binding) {
ngModelAttrs[camelize(binding)] = {
bound: binding
};
});
formlyConfig.setType({
name: 'datepicker',
templateUrl: 'datepicker.html',
wrapper: ['bootstrapLabel', 'bootstrapHasError'],
defaultOptions: {
ngModelAttrs: ngModelAttrs,
templateOptions: {
datepickerOptions: {
format: 'MM.dd.yyyy'
}
}
},
controller: ['$scope',
function($scope) {
$scope.datepicker = {};
$scope.datepicker.opened = false;
$scope.datepicker.open = function($event) {
$scope.datepicker.opened = !$scope.datepicker.opened;
};
}
]
});
function camelize(string) {
string = string.replace(/[\-_\s]+(.)?/g, function(match, chr) {
return chr ? chr.toUpperCase() : '';
});
// Ensure 1st char is always lowercase
return string.replace(/^([A-Z])/, function(match, chr) {
return chr ? chr.toLowerCase() : '';
});
}
});
app.controller('MainCtrl', function MainCtrl(formlyVersion) {
var vm = this;
// funcation assignment
vm.onSubmit = onSubmit;
// variable assignment
vm.author = { // optionally fill in your info below :-)
name: 'Kent C. Dodds',
url: 'https://twitter.com/kentcdodds'
};
vm.exampleTitle = 'UI Bootstrap Datepicker'; // add this
vm.env = {
angularVersion: angular.version.full,
formlyVersion: formlyVersion
};
vm.model = {
year: 2005,
text: 'hello',
number: '123.4'
};
vm.options = {};
vm.fields = [{
key: 'year',
type: 'datepicker',
templateOptions: {
label: 'Year',
type: 'text',
datepickerPopup: 'dd-MMMM-yyyy',
datepickerOptions: {
format: 'yyyy',
minMode: 'year'
}
},
parsers: [dateToYear],
formatters: [yearToDate]
}, {
key: 'text',
type: 'input',
templateOptions: {
label: 'Text',
},
parsers: [toLowerCase],
formatters: [toUpperCase]
}, {
key: 'number',
type: 'input',
templateOptions: {
label: 'Number',
type: 'number'
},
formatters: [strToNumber]
}];
vm.originalFields = angular.copy(vm.fields);
// function definition
function onSubmit() {
vm.options.updateInitialValue();
alert(JSON.stringify(vm.model), null, 2);
}
function yearToDate(value) {
console.log('yearToDate: ' + value);
if (!value) {
return new Date();
}
return (typeof value === 'object') ? value : (new Date()).setFullYear(value);
}
function dateToYear(value) {
console.log('dateToYear: ' + value);
return (typeof value === 'object') ? value.getFullYear() : null;
}
function toUpperCase(value) {
console.log('toUpperCase: ' + value);
return (value || '').toUpperCase();
}
function toLowerCase(value) {
console.log('toLowerCase: ' + value);
return (value || '').toLowerCase();
}
function strToNumber(value) {
console.log('strToNumber: ' + value);
return parseFloat(value);
}
});
})();
body {
margin: 20px
}
.formly-field {
margin-bottom: 16px;
}
<!DOCTYPE html>
<html>
<head>
<!-- Twitter bootstrap -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet">
<!-- apiCheck is used by formly to validate its api -->
<script src="//npmcdn.com/api-check#latest/dist/api-check.js"></script>
<!-- This is the latest version of angular (at the time this template was created) -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.js"></script>
<!-- Angular-UI Bootstrap has tabs directive we want -->
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/1.3.2/ui-bootstrap-tpls.js"></script>
<!-- This is the latest version of formly core. -->
<script src="//npmcdn.com/angular-formly#latest/dist/formly.js"></script>
<!-- This is the latest version of formly bootstrap templates -->
<script src="//npmcdn.com/angular-formly-templates-bootstrap#latest/dist/angular-formly-templates-bootstrap.js"></script>
<title>Angular Formly Example</title>
</head>
<body ng-app="formlyExample" ng-controller="MainCtrl as vm">
<div>
<h1>angular-formly example: {{vm.exampleTitle}}</h1>
<div>
This is a <strong>very</strong> basic example of how to use the
UI Boostrap datepicker
with angular-formly.
</div>
<hr />
<form ng-submit="vm.onSubmit()" name="vm.form" novalidate>
<formly-form model="vm.model" fields="vm.fields" options="vm.options" form="vm.form">
<button type="submit" class="btn btn-primary submit-button" ng-disabled="vm.form.$invalid">Submit</button>
<button type="button" class="btn btn-default" ng-click="vm.options.resetModel()">Reset</button>
</formly-form>
</form>
<hr />
<h2>Model Value</h2>
<pre>{{vm.model | json}}</pre>
<h2>Fields <small>(note, functions are not shown)</small></h2>
<pre>{{vm.originalFields | json}}</pre>
<h2>Form</h2>
<pre>{{vm.form | json}}</pre>
</div>
<div style="margin-top:30px">
<small>
This is an example for the
angular-formly
project made with ♥ by
<strong>
<span ng-if="!vm.author.name || !vm.author.url">
{{vm.author.name || 'anonymous'}}
</span>
<a ng-if="vm.author.url" ng-href="{{::vm.author.url}}">
{{vm.author.name}}
</a>
</strong>
<br />
This example is running angular version "{{vm.env.angularVersion}}" and formly version "{{vm.env.formlyVersion}}"
</small>
</div>
<!-- Put custom templates here -->
<script type="text/ng-template" id="datepicker.html">
<p class="input-group">
<input type="text" id="{{::id}}" name="{{::id}}" ng-model="model[options.key]" class="form-control" ng-click="datepicker.open($event)" uib-datepicker-popup="{{to.datepickerOptions.format}}" is-open="datepicker.opened" datepicker-options="to.datepickerOptions"
/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="datepicker.open($event)" ng-disabled="to.disabled"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</script>
</body>
</html>
Also problem described in this issue
Maybe somebody can help me.
Appreciate your help.
Related
I am working on Angularjs form validation for multiple input values.
Below is the issue I am facing
Let's say I entered 'Johnss', directive will validate and give error Invalid name
click on plus(+) button adjacent to Name label to add new Name input.
Enter Peter. Now you will see "Invalid name" for both Peter and Johnss.
Click on cancel button next to Peter.
Now in place of 'Johnss' try to enter any value and no validation is being done. Whatever value you enter it is accepting and no validation is happening.
Same is the case with 'Place' label.
What is the better way of doing this validation on each of the element?. Can someone please help me on this.
Also I am looking for a way to disable + button when validation fails.
Below is the code I have written
JS
var app = angular.module('multipleInputs',[]);
app.controller('multipleInputsCtrl',function($scope) {
$scope.users = [
{}
];
$scope.places = [{}];
$scope.addUser = function() {
var newUser = {};
$scope.users.push(newUser);
}
$scope.removeUser = function(user) {
var index = $scope.users.indexOf(user);
$scope.users.splice(index,1);
}
$scope.addPlace = function() {
var newPlace = {};
$scope.places.push(newPlace);
}
$scope.removePlace = function(place) {
var index = $scope.places.indexOf(place);
$scope.places.splice(index,1);
}
});
app.directive('nameValidation', function() {
var acceptableNames = ['John', 'Sachin', 'Sam', 'Sudhir', 'Nalanda'];
return {
require: 'ngModel',
link: function(scope, element, attr, mCtrl) {
mCtrl.$validators.name = function validationError(value) {
if (value && acceptableNames.includes(value)) {
return true;
} else if (value && !acceptableNames.includes(value)) {
return false;
} else {
return true;
}
}
}
};
})
app.directive('placeValidation', function() {
var acceptableNames = ['Chicago', 'Houston', 'Dallas', 'Seattle', 'Mumbai'];
return {
require: 'ngModel',
link: function(scope, element, attr, mCtrl) {
mCtrl.$validators.place = function validationError(value) {
if (value && acceptableNames.includes(value)) {
return true;
} else if (value && !acceptableNames.includes(value)) {
return false;
} else {
return true;
}
}
}
};
})
HTML
<!doctype html>
<html>
<head>
<title>Generate Multiple Input Fields Dynamically</title>
<link rel="stylesheet" href="styles.css" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.8/angular.min.js"></script>
<script src="multipleInputs.js"></script>
</head>
<body ng-app="multipleInputs" class="users-container" ng-controller="multipleInputsCtrl">
<form name="myForm">
<div>
<div class="form-group">
<label for="inputName" style="color:darkblue">Name <button type="button" class="btn btn-sm" ng-click="addUser()">+</button></label>
<section ng-repeat="user in users">
<input class="users-container-inputs-text" type="text" placeholder="First Name" ng-model="user.name" name="personName" name-validation>
<span class="users-container-inputs-button" ng-click="removeUser(user)">X
</span>
<span class="alert-danger" ng-show="myForm.personName.$error.name"> Invalid name</span>
</input>
</section>
</div>
<div class="form-group">
<label for="inputPlace" style="color:darkblue">Place <button type="button" class="btn btn-sm" ng-click="addPlace()">+</button></label>
<section ng-repeat="place in places">
<input class="users-container-inputs-text" type="text" placeholder="Place" ng-model="place.name" name="placeName" place-validation>
<span class="users-container-inputs-button" ng-click="removePlace(place)">X
</span>
<span class="alert-danger" ng-show="myForm.placeName.$error.place"> Invalid place</span>
</input>
</section>
</div>
</div>
</form>
</body>
</html>
Codepen link:
https://codepen.io/sudhir_rocks/pen/ZEpKVrR
You can use ng-disabled Directive in AngularJS to disable HTML elements.
https://docs.angularjs.org/api/ng/directive/ngDisabled
If the expression inside the ng-disabled attribute returns true then the form field will be disabled.
1.i want to remove Link in the method from a vue.js component
please helm me..error in console is method splice is undefined.
link will add when the message in insert.message is not a problem.
insert link in not a problem.but it's not possible to remove.
push array in my single page.but if is not good for the user is possible to remove
<div class="list-group">
<div class="col-lg-4" style="margin-
top:3px">
<input type="text" v-model="link.title"
class="form-control" placeholder="titolo" id="title">
</div>
<div class="col-lg-7">
<input type="text" v-model="link.hyperlink"
class="form-control" placeholder="link" id="link">
</div>
<div class="col-lg-1">
<button #click="addLink" type="button"
id="add-link-btn" class="btn btn btn-primary pull-
right">+</button>
</div>
</div>
</div>
<div v-for="link in message.links"
:key="link.id">
<div class="row">
<div class="col-lg-6">
<p>{{link.title}}</p>
</div>
<div class="col-lg-6">
<a>{{link.hyperlink}}</a>
<button class="btn btn-xs btn-danger"
#click="removeLink(link)">Delete</button>
</div>
</div>
<scrip>
data() {
return {
title: "Aggiungi",
link: {
id: 1,
autore: "Amedeo",
title: "",
hyperlink: ""
},
}
}
methods: {
addMessage() {
var id = this.messages.length
? this.messages[this.messages.length - 1].id
: 0;
var message = Object.assign({}, this.message);
message.id = id + 1;
message.date = new Date();
this.messages.push(message);
this.message.title = "";
this.message.subtitle = "";
this.message.body = "";
},
// metodo addlink che inserisci un nuovo link ovvimente lavorando
sul id del messaggio padre
addLink() {
var messageId = this.messages.length
? this.messages[this.messages.length - 1].id
: 1;
var id = this.message.links.length
? this.message.links[this.message.links.length - 1].id
: parseInt(messageId + "0", 10);
var link = Object.assign({}, this.link);
link.id = id + 1;
link.date = new Date();
this.message.links.push(link);
this.link.title = "";
this.link.hyperlink = "";
},
removeLink(link) {
this.links.splice(this.links.indexOf(link), 1);
}
}
};
You need to pre-define every property you will access in data.
Due to the limitations of modern JavaScript (and the abandonment of
Object.observe), Vue cannot detect property addition or deletion.
https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
In your code messages and links are not defined in your data object at the start, so the reactivity will not work.
For example, the following code doesn't work :
<div id="app">
Message: {{message}}<br />
<input type="text" v-on:input="update($event.target.value)" />
</div>
<script>
new Vue({
el: '#app',
data: {
},
methods: {
update: function(value) {
this.message = value;
}
}
});
</script>
https://jsfiddle.net/m4q44g7f/
But this code works because message is defined at the start.
<div id="app">
Message: {{message}}<br />
<input type="text" v-on:input="update($event.target.value)" />
</div>
<script>
new Vue({
el: '#app',
data: {
message: ''
},
methods: {
update: function(value) {
this.message = value;
}
}
});
</script>
https://jsfiddle.net/m4q44g7f/1/
Note: There might be other errors in your code but you first need to fix this one.
If user clicks 'add data' a modal opens, I have individual checkboxes for data1,data2,data3,data4. I also have a check all checkbox. If user clicks this, it should be able to select all above 4, if clicks again, it should deselect all. Also, once selected, the value of 'Select All' should change to 'Deselect All'
Code:
<div ng-controller="ModalDemoCtrl">
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-body">
<label><input type="checkbox" ng-model="checkboxModel.value1"> Data1</label><br/>
<label><input type="checkbox" ng-model="checkboxModel.value2"> Data2</label><br/>
<label><input type="checkbox" ng-model="checkboxModel.value3"> Data3</label><br/>
<label><input type="checkbox" ng-model="checkboxModel.value4"> Data4</label><br/>
<label><input type="checkbox" ng-model="checkboxModel.value5">Select All</label><br/>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>
<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>
</div>
</script>
<button type="button" class="btn btn-default" ng-click="open()">Add data</button>
</div>
Can someone let me know how to achieve this. I saw a lot of posts online but each had ng-repeat for the first 4 checkboxes. Mine is without using ng-repeat. Can someone please advise.
I have the following plunker: http://plnkr.co/edit/nSCGJzj1zG5SGO2N76L3?p=preview
I noticed that you were not binding to the items array. I fixed that by using the following markup:
<div ng-repeat="item in items">
<label>
<input type="checkbox" ng-model="item.Selected">{{item.name}}</label>
<br/>
</div>
<label>
<input type="checkbox" ng-model="selectedAll" ng-click="checkAll()">Select All</label>
<br/>
After that, I had to change the structure of the items property to become an object for later binding:
$scope.items = [{
name: 'item 1'
}, {
name: 'item 2'
}, {
name: 'item 3'
}, ];
To be able to update all the other checkboxes, I resorted to the following code:
$scope.checkAll = function() {
if ($scope.selectedAll) {
$scope.selectedAll = true;
} else {
$scope.selectedAll = false;
}
angular.forEach($scope.items, function(item) {
item.Selected = $scope.selectedAll;
});
}
Please, refer to this plunker example for the full working solution.
UPDATE
Use the example here:
https://docs.angularjs.org/api/ng/directive/ngChecked#!
basically the trick is to add to all "slave" check boxes
ng-checked="checkboxModel.value5"
Well, first the explanation:
To select/deselect all the checkboxes, you can simply loop over the elements and setting all the checkbox to true/false.
To select/deselect this checkbox programatically, you can use Array.prototype.every() method, which returns if all checkboxes are selected or not, if all elements are selected that option will be/keep also selected, otherwise, it will be/keep deselected.
To get all the selected items when you close the modal you could use the Array.prototype.filter() method to return only the selected checkboxes.
Here's a snippet working:
(function() {
"use strict";
angular
.module('ui.bootstrap.demo', [
'ngAnimate',
'ui.bootstrap'
])
.controller('ModalDemoCtrl', ModalDemoCtrl)
.controller('ModalInstanceCtrl', ModalInstanceCtrl);
function ModalDemoCtrl($scope, $uibModal, $log) {
$scope.animationsEnabled = true;
$scope.open = function(size) {
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return $scope.items;
}
}
});
modalInstance.result.then(function(selectedItems) {
$scope.selected = selectedItems;
}, function() {
$log.info('Modal dismissed at: ', new Date());
});
}
$scope.toggleAnimation = function() {
$scope.animationsEnabled = !$scope.animationsEnabled;
}
}
// Please note that $uibModalInstance represents a modal window (instance) dependency.
// It is not the same as the $uibModal service used above.
function ModalInstanceCtrl($scope, $uibModalInstance, items) {
$scope.items = [];
function loadData() {
var arr = [];
for (var i = 1; i <= 5; i++) {
arr.push({
"name": "Item " + i
});
}
return arr;
}
$scope.items = loadData();
$scope.check = function() {
$scope.selectedAll = $scope.items.every(function(value) {
return value.Selected;
});
}
$scope.selectAll = function() {
$scope.items.forEach(function(item) {
item.Selected = $scope.selectedAll;
});
}
function getChecked() {
return $scope.items.filter(function(value) {
return value.Selected;
});
}
$scope.ok = function() {
$uibModalInstance.close(getChecked());
}
$scope.cancel = function() {
$uibModalInstance.dismiss('cancel');
}
}
})();
.main_div {
border: 1px solid red;
width: 30%;
margin: 50px;
padding: 2px;
}
<!DOCTYPE HTML>
<html ng-app="ui.bootstrap.demo">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular-animate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.0.0/ui-bootstrap-tpls.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" />
</head>
<body ng-controller="ModalDemoCtrl">
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-body">
<div class="checkbox" ng-repeat="item in items">
<label>
<input type="checkbox" ng-model="item.Selected" ng-change="check()"> {{item.name}}
</label>
<br/>
</div>
<div class="checkbox">
<label>
<input type="checkbox" ng-model="selectedAll" ng-click="selectAll()"> Select All
</label>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>
<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>
</div>
</script>
<button type="button" class="btn btn-default" ng-click="open()">Add data!</button>
</body>
</html>
If you want to see it in plunker, click here.
I hope it helps!!
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>Batch editable table</title>
<link rel='stylesheet prefetch' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css'>
<link rel='stylesheet prefetch' href='https://cdn.rawgit.com/esvit/ng-table/v0.8.1/dist/ng-table.min.css'>
<link rel='stylesheet prefetch' href='css/nqjzro.css'>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div ng-app="myApp" class="container-fluid">
<div class="row">
<div class="col-xs-12">
<h2 class="page-header">Batch editable table</h2>
<div class="row">
<div class="col-md-6">
<div class="bs-callout bs-callout-info">
<h4>Overview</h4>
<p>Example of how to create a batch editable table.</p>
</div>
</div>
<div class="col-md-6">
<div class="bs-callout bs-callout-warning">
<h4>Notice</h4>
<p>There are several directives in use that track dirty state and validity of the table rows. Whilst they are reasonally capable they are <em>not production tested - use at your own risk!</em> More details...</p>
<div ng-show="isExplanationOpen">
<p>If you look at the declarative markup for the <code>ngTable</code> you'll see a bunch of nested <code>ngForm</code> directives, with a common ancestor <code>ngForm</code> at the level of the table element. Each nested <code>ngForm</code> propogates their <code>$dirty</code> and <code>$invalid</code> state to this top level <code>ngForm</code>. This works great as you can enable/disable the buttons for saving the table based on the status of this single top-level <code>ngForm</code>.</p>
<p>This works up till the point that the user select's a new page to display in the table. At which point the existing nested <code>ngForm</code> directives are swapped out for new instances as the new data page is loaded. These new <code>ngForm</code> directives are always pristine and valid and this status propogates setting the corrosponding state on the top-level to be pristine and valid even though rows from the previous page are dirty and possibly invalid.</p>
<p>The solution is to have a set of directives that sit parallel to the <code>ngForm</code> directives that remember the state of the rows when the corrosponding <code>ngFrom</code> directives are destroyed and recreated. When <code>ngForm</code> directives are recreated they have their status reset by the directives that have remembered this state.</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6" ng-controller="demoController as demo">
<h3>ngTable directive</h3>
<div class="brn-group pull-right">
<button class="btn btn-default" ng-if="demo.isEditing" ng-click="demo.cancelChanges()">
<span class="glyphicon glyphicon-remove"></span>
</button>
<button class="btn btn-primary" ng-if="!demo.isEditing" ng-click="demo.isEditing = true">
<span class="glyphicon glyphicon-pencil"></span>
</button>
<button class="btn btn-primary" ng-if="demo.isEditing" ng-disabled="!demo.hasChanges() || demo.tableTracker.$invalid" ng-click="demo.saveChanges()">
<span class="glyphicon glyphicon-ok"></span>
</button>
<button class="btn btn-default" ng-click="demo.add()">
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
<table ng-table="demo.tableParams" class="table table-bordered table-hover table-condensed editable-table" ng-form="demo.tableForm" disable-filter="demo.isAdding" demo-tracked-table="demo.tableTracker">
<colgroup>
<col width="70%" />
<col width="12%" />
<col width="13%" />
<col width="5%" />
</colgroup>
<tr ng-repeat="row in $data" ng-form="rowForm" demo-tracked-table-row="row">
<td title="'Name'" filter="{name: 'text'}" sortable="'name'" ng-switch="demo.isEditing" ng-class="name.$dirty ? 'bg-warning' : ''" ng-form="name" demo-tracked-table-cell>
<span ng-switch-default class="editable-text">{{row.name}}</span>
<div class="controls" ng-class="name.$invalid && name.$dirty ? 'has-error' : ''" ng-switch-when="true">
<input type="text" name="name" ng-model="row.name" class="editable-input form-control input-sm" required />
</div>
</td>
<td title="'Age'" filter="{age: 'number'}" sortable="'age'" ng-switch="demo.isEditing" ng-class="age.$dirty ? 'bg-warning' : ''" ng-form="age" demo-tracked-table-cell>
<span ng-switch-default class="editable-text">{{row.age}}</span>
<div class="controls" ng-class="age.$invalid && age.$dirty ? 'has-error' : ''" ng-switch-when="true">
<input type="number" name="age" ng-model="row.age" class="editable-input form-control input-sm" required/>
</div>
</td>
<td title="'Money'" filter="{money: 'number'}" sortable="'money'" ng-switch="demo.isEditing" ng-class="money.$dirty ? 'bg-warning' : ''" ng-form="money" demo-tracked-table-cell>
<span ng-switch-default class="editable-text">{{row.money}}</span>
<div class="controls" ng-class="money.$invalid && money.$dirty ? 'has-error' : ''" ng-switch-when="true">
<input type="number" name="money" ng-model="row.money" class="editable-input form-control input-sm" required/>
</div>
</td>
<td>
<button class="btn btn-danger btn-sm" ng-click="demo.del(row)" ng-disabled="!demo.isEditing"><span class="glyphicon glyphicon-trash"></span></button>
</td>
</tr>
</table>
</div>
<div class="col-md-6" ng-controller="dynamicDemoController as demo">
<h3>ngTableDynamic directive</h3>
<div class="brn-group pull-right">
<button class="btn btn-default" ng-if="demo.isEditing" ng-click="demo.cancelChanges()">
<span class="glyphicon glyphicon-remove"></span>
</button>
<button class="btn btn-primary" ng-if="!demo.isEditing" ng-click="demo.isEditing = true">
<span class="glyphicon glyphicon-pencil"></span>
</button>
<button class="btn btn-primary" ng-if="demo.isEditing" ng-disabled="!demo.hasChanges() || demo.tableTracker.$invalid" ng-click="demo.saveChanges()">
<span class="glyphicon glyphicon-ok"></span>
</button>
<button class="btn btn-default" ng-click="demo.add()">
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
<table ng-table-dynamic="demo.tableParams with demo.cols" class="table table-bordered table-condensed table-hover editable-table" ng-form="demo.tableForm" disable-filter="demo.isAdding" demo-tracked-table="demo.tableTracker">
<colgroup>
<col width="70%" />
<col width="12%" />
<col width="13%" />
<col width="5%" />
</colgroup>
<tr ng-repeat="row in $data" ng-form="rowForm" demo-tracked-table-row="row">
<td ng-repeat="col in $columns" ng-class="rowForm[col.field].$dirty ? 'bg-warning' : ''" ng-form="{{col.field}}" demo-tracked-table-cell>
<span ng-if="col.dataType !== 'command' && !demo.isEditing" class="editable-text">{{row[col.field]}}</span>
<div ng-if="col.dataType !== 'command' && demo.isEditing" class="controls" ng-class="rowForm[col.field].$invalid && rowForm[col.field].$dirty ? 'has-error' : ''" ng-switch="col.dataType">
<input ng-switch-default type="text" name="{{col.field}}" ng-model="row[col.field]" class="editable-input form-control input-sm" required />
<input ng-switch-when="number" type="number" name="{{col.field}}" ng-model="row[col.field]" class="editable-input form-control input-sm" required />
</div>
<button ng-if="col.dataType === 'command'" class="btn btn-danger btn-sm" ng-click="demo.del(row)" ng-disabled="!demo.isEditing"><span class="glyphicon glyphicon-trash"></span></button>
</td>
</tr>
</table>
</div>
</div>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js'></script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.4/angular.min.js'></script>
<script src='https://rawgit.com/esvit/ng-table/master/dist/ng-table.min.js'></script>
<script src="js/index.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.8/angular-route.js"></script>
</body>
</html>
The above is html code
angular.module("myApp", ["ngTable", "ngTableDemos"]);
(function() {
"use strict";
angular.module("myApp").controller("demoController", demoController);
demoController.$inject = ["NgTableParams", "ngTableSimpleList"];
function demoController(NgTableParams, simpleList) {
var self = this;
var originalData = angular.copy(simpleList);
self.tableParams = new NgTableParams({}, {
dataset: angular.copy(simpleList)
});
self.deleteCount = 0;
self.add = add;
self.cancelChanges = cancelChanges;
self.del = del;
self.hasChanges = hasChanges;
self.saveChanges = saveChanges;
//////////
function add() {
self.isEditing = true;
self.isAdding = true;
self.tableParams.settings().dataset.unshift({
name: "",
age: null,
money: null
});
// we need to ensure the user sees the new row we've just added.
// it seems a poor but reliable choice to remove sorting and move them to the first page
// where we know that our new item was added to
self.tableParams.sorting({});
self.tableParams.page(1);
self.tableParams.reload();
}
function cancelChanges() {
resetTableStatus();
var currentPage = self.tableParams.page();
self.tableParams.settings({
dataset: angular.copy(originalData)
});
// keep the user on the current page when we can
if (!self.isAdding) {
self.tableParams.page(currentPage);
}
}
function del(row) {
_.remove(self.tableParams.settings().dataset, function(item) {
return row === item;
});
self.deleteCount++;
self.tableTracker.untrack(row);
self.tableParams.reload().then(function(data) {
if (data.length === 0 && self.tableParams.total() > 0) {
self.tableParams.page(self.tableParams.page() - 1);
self.tableParams.reload();
}
});
}
function hasChanges() {
return self.tableForm.$dirty || self.deleteCount > 0
}
function resetTableStatus() {
self.isEditing = false;
self.isAdding = false;
self.deleteCount = 0;
self.tableTracker.reset();
self.tableForm.$setPristine();
}
function saveChanges() {
resetTableStatus();
var currentPage = self.tableParams.page();
originalData = angular.copy(self.tableParams.settings().dataset);
}
}
})();
(function() {
"use strict";
angular.module("myApp").controller("dynamicDemoController", dynamicDemoController);
dynamicDemoController.$inject = ["NgTableParams", "ngTableSimpleList"];
function dynamicDemoController(NgTableParams, simpleList) {
var self = this;
var originalData = angular.copy(simpleList);
self.cols = [{
field: "name",
title: "Name",
filter: {
name: "text"
},
sortable: "name",
dataType: "text"
}, {
field: "age",
title: "Age",
filter: {
age: "number"
},
sortable: "age",
dataType: "number"
}, {
field: "money",
title: "Money",
filter: {
money: "number"
},
sortable: "money",
dataType: "number"
}, {
field: "action",
title: "",
dataType: "command"
}];
self.tableParams = new NgTableParams({}, {
dataset: angular.copy(simpleList)
});
self.deleteCount = 0;
self.add = add;
self.cancelChanges = cancelChanges;
self.del = del;
self.hasChanges = hasChanges;
self.saveChanges = saveChanges;
//////////
function add() {
self.isEditing = true;
self.isAdding = true;
self.tableParams.settings().dataset.unshift({
name: "",
age: null,
money: null
});
// we need to ensure the user sees the new row we've just added.
// it seems a poor but reliable choice to remove sorting and move them to the first page
// where we know that our new item was added to
self.tableParams.sorting({});
self.tableParams.page(1);
self.tableParams.reload();
}
function cancelChanges() {
resetTableStatus();
var currentPage = self.tableParams.page();
self.tableParams.settings({
dataset: angular.copy(originalData)
});
// keep the user on the current page when we can
if (!self.isAdding) {
self.tableParams.page(currentPage);
}
}
function del(row) {
_.remove(self.tableParams.settings().dataset, function(item) {
return row === item;
});
self.deleteCount++;
self.tableTracker.untrack(row);
self.tableParams.reload().then(function(data) {
if (data.length === 0 && self.tableParams.total() > 0) {
self.tableParams.page(self.tableParams.page() - 1);
self.tableParams.reload();
}
});
}
function hasChanges() {
return self.tableForm.$dirty || self.deleteCount > 0
}
function resetTableStatus() {
self.isEditing = false;
self.isAdding = false;
self.deleteCount = 0;
self.tableTracker.reset();
self.tableForm.$setPristine();
}
function saveChanges() {
resetTableStatus();
var currentPage = self.tableParams.page();
originalData = angular.copy(self.tableParams.settings().dataset);
}
}
})();
(function() {
"use strict";
angular.module("myApp").run(configureDefaults);
configureDefaults.$inject = ["ngTableDefaults"];
function configureDefaults(ngTableDefaults) {
ngTableDefaults.params.count = 5;
ngTableDefaults.settings.counts = [];
}
})();
/**********
The following directives are necessary in order to track dirty state and validity of the rows
in the table as the user pages within the grid
------------------------
*/
(function() {
angular.module("myApp").directive("demoTrackedTable", demoTrackedTable);
demoTrackedTable.$inject = [];
function demoTrackedTable() {
return {
restrict: "A",
priority: -1,
require: "ngForm",
controller: demoTrackedTableController
};
}
demoTrackedTableController.$inject = ["$scope", "$parse", "$attrs", "$element"];
function demoTrackedTableController($scope, $parse, $attrs, $element) {
var self = this;
var tableForm = $element.controller("form");
var dirtyCellsByRow = [];
var invalidCellsByRow = [];
init();
////////
function init() {
var setter = $parse($attrs.demoTrackedTable).assign;
setter($scope, self);
$scope.$on("$destroy", function() {
setter(null);
});
self.reset = reset;
self.isCellDirty = isCellDirty;
self.setCellDirty = setCellDirty;
self.setCellInvalid = setCellInvalid;
self.untrack = untrack;
}
function getCellsForRow(row, cellsByRow) {
return _.find(cellsByRow, function(entry) {
return entry.row === row;
})
}
function isCellDirty(row, cell) {
var rowCells = getCellsForRow(row, dirtyCellsByRow);
return rowCells && rowCells.cells.indexOf(cell) !== -1;
}
function reset() {
dirtyCellsByRow = [];
invalidCellsByRow = [];
setInvalid(false);
}
function setCellDirty(row, cell, isDirty) {
setCellStatus(row, cell, isDirty, dirtyCellsByRow);
}
function setCellInvalid(row, cell, isInvalid) {
setCellStatus(row, cell, isInvalid, invalidCellsByRow);
setInvalid(invalidCellsByRow.length > 0);
}
function setCellStatus(row, cell, value, cellsByRow) {
var rowCells = getCellsForRow(row, cellsByRow);
if (!rowCells && !value) {
return;
}
if (value) {
if (!rowCells) {
rowCells = {
row: row,
cells: []
};
cellsByRow.push(rowCells);
}
if (rowCells.cells.indexOf(cell) === -1) {
rowCells.cells.push(cell);
}
} else {
_.remove(rowCells.cells, function(item) {
return cell === item;
});
if (rowCells.cells.length === 0) {
_.remove(cellsByRow, function(item) {
return rowCells === item;
});
}
}
}
function setInvalid(isInvalid) {
self.$invalid = isInvalid;
self.$valid = !isInvalid;
}
function untrack(row) {
_.remove(invalidCellsByRow, function(item) {
return item.row === row;
});
_.remove(dirtyCellsByRow, function(item) {
return item.row === row;
});
setInvalid(invalidCellsByRow.length > 0);
}
}
})();
(function() {
angular.module("myApp").directive("demoTrackedTableRow", demoTrackedTableRow);
demoTrackedTableRow.$inject = [];
function demoTrackedTableRow() {
return {
restrict: "A",
priority: -1,
require: ["^demoTrackedTable", "ngForm"],
controller: demoTrackedTableRowController
};
}
demoTrackedTableRowController.$inject = ["$attrs", "$element", "$parse", "$scope"];
function demoTrackedTableRowController($attrs, $element, $parse, $scope) {
var self = this;
var row = $parse($attrs.demoTrackedTableRow)($scope);
var rowFormCtrl = $element.controller("form");
var trackedTableCtrl = $element.controller("demoTrackedTable");
self.isCellDirty = isCellDirty;
self.setCellDirty = setCellDirty;
self.setCellInvalid = setCellInvalid;
function isCellDirty(cell) {
return trackedTableCtrl.isCellDirty(row, cell);
}
function setCellDirty(cell, isDirty) {
trackedTableCtrl.setCellDirty(row, cell, isDirty)
}
function setCellInvalid(cell, isInvalid) {
trackedTableCtrl.setCellInvalid(row, cell, isInvalid)
}
}
})();
(function() {
angular.module("myApp").directive("demoTrackedTableCell", demoTrackedTableCell);
demoTrackedTableCell.$inject = [];
function demoTrackedTableCell() {
return {
restrict: "A",
priority: -1,
scope: true,
require: ["^demoTrackedTableRow", "ngForm"],
controller: demoTrackedTableCellController
};
}
demoTrackedTableCellController.$inject = ["$attrs", "$element", "$scope"];
function demoTrackedTableCellController($attrs, $element, $scope) {
var self = this;
var cellFormCtrl = $element.controller("form");
var cellName = cellFormCtrl.$name;
var trackedTableRowCtrl = $element.controller("demoTrackedTableRow");
if (trackedTableRowCtrl.isCellDirty(cellName)) {
cellFormCtrl.$setDirty();
} else {
cellFormCtrl.$setPristine();
}
// note: we don't have to force setting validaty as angular will run validations
// when we page back to a row that contains invalid data
$scope.$watch(function() {
return cellFormCtrl.$dirty;
}, function(newValue, oldValue) {
if (newValue === oldValue) return;
trackedTableRowCtrl.setCellDirty(cellName, newValue);
});
$scope.$watch(function() {
return cellFormCtrl.$invalid;
}, function(newValue, oldValue) {
if (newValue === oldValue) return;
trackedTableRowCtrl.setCellInvalid(cellName, newValue);
});
}
})();
This is javascript file
The above code is angularjs. It displays table loading data with add row, delete, edit.
Please solve this. Am getting error [$injector:modulerr] . Please help me to solve
Edit: They created different codepen for ngTableDemos look here
To run your sample you need to load that ngTableDemos related code first then your sample.
For codepen, to understand what are the files they are using to run any sample check Pen Setting JavaScript tab carefully.
Probably you didn't load "ngTableDemos" module, so if it is needed load associate file or remove it like
angular.module("myApp", ["ngTable"]);
Using Javascript with angularJs, I kind the following code :
JS
$scope.myObj = {
'sthg': '',
'a': [{
'b' : ''
}]
}
HTML
<p ng-repeat="radio in fiche.radios">
<input type="text" ng-model="radio.code" placeholder="Numéro de cliché" ng-required="true" />
<span >
<button type="button"ng-click="removeRadioList($index);" ng-disabled="fiche.radios.length === 1">
<i>X</i>
</button>
</span>
</p>
http://plnkr.co/edit/LOgk7Nudse0srS7Bs1G6?p=preview
In my App $scope.myObj.a[0].b is undefined with the ng-repeat (if I remove ng-repeat, it will be defined).
In the plunkr it is defined even after the run of ng-repeat, but I managed to have the behaviour when I enter something in the input and then delete it.
I don't get what's hapening, I would like to understand why and if it is a good way to do ?
Beacause you set ng-required="true" on your input tag angular wouldn't bind empty value to your model thous when you delete value from input your console shows you radios.code as undefined.
Please see below for working demo:
(function() {
"use strict";
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $log) {
$scope.ficheInit = {
'user_id': '',
'radios': [{
'code': ''
}]
};
$log.log($scope.ficheInit);
$scope.fiche = angular.copy($scope.ficheInit);
$log.log($scope.fiche);
$scope.addRadioList = function() {
$scope.fiche.radios.push({
'code': ''
});
}
$scope.removeRadioList = function(i) {
$scope.fiche.radios.splice(i, 1);
}
$scope.disableAddRadio = function() {
console.log($scope.fiche.radios);
return !(angular.isDefined($scope.fiche.radios[$scope.fiche.radios.length - 1].code) || $scope.fiche.radios.length < 1);
}
});
}());
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="plunker">
<div ng-controller="MainCtrl">
<button ng-click="showForm=true;">SHOW</button>
<div ng-show="showForm">
<button ng-click="addRadioList();" ng-disabled="disableAddRadio()">Ajouter un cliché</button>
<p ng-repeat="radio in fiche.radios">
<input type="text" ng-model="radio.code" placeholder="Numéro de cliché" />
<span>
<button type="button" ng-click="removeRadioList($index);" ng-disabled="fiche.radios.length === 1">
<i>X</i>
</button>
</span>
</p>
</div>
{{fiche.radios}}
</div>
</div>