How to $watch changes on models created by ng-repeat? - angularjs

Consider this Plnkr for example. I don't know how many members of fooCollection will be created beforehand. So I don't know how many bar models are going to exist.
But I know they are going to be angular models, and I know where they are going to be.
How do I do a $watch on these?
I need to do that because I need to trigger behavior when a bar model is changed. Watching the fooCollection itself is not enough, the $watch listener does not fire when a bar is changed.
Relevant html:
<body ng-controller="testCtrl">
<div ng-repeat="(fooKey, foo) in fooCollection">
Tell me your name: <input ng-model="foo.bar">
<br />
Hello, my name is {{ foo.bar }}
</div>
<button ng-click="fooCollection.push([])">Add a Namer</button>
</body>
Relevant JS:
angular
.module('testApp', [])
.controller('testCtrl', function ($scope) {
$scope.fooCollection = [];
$scope.$watch('fooCollection', function (oldValue, newValue) {
if (newValue != oldValue)
console.log(oldValue, newValue);
});
});

Create individual list-item controllers: demo on Plnkr
js
angular
.module('testApp', [])
.controller('testCtrl', function ($scope) {
$scope.fooCollection = [];
})
.controller('fooCtrl', function ($scope) {
$scope.$watch('foo.bar', function (newValue, oldValue) {
console.log('watch fired, new value: ' + newValue);
});
});
HTML
<html ng-app="testApp">
<body ng-controller="testCtrl">
<div ng-repeat="(fooKey, foo) in fooCollection" ng-controller="fooCtrl">
Tell me your name: <input ng-model="foo.bar" ng-change="doSomething()">
<br />
Hello, my name is {{ foo.bar }}
</div>
<button ng-click="fooCollection.push([])">Add a Namer</button>
</body>
</html>

If you have your collection populated, you can place a watch on each item of the ng-repeat:
html
<div ng-repeat="item in items">
{{ item.itemField }}
</div>
js
for (var i = 0; i < $scope.items.length; i++) {
$scope.$watch('items[' + i + ']', function (newValue, oldValue) {
console.log(newValue.itemField + ":::" + oldValue.itemField);
}, true);
}

You can pass true as third argument into $watch
$scope.$watch('something', function() { doSomething(); }, true);
https://docs.angularjs.org/api/ng/type/$rootScope.Scope

You can also create a custom directive that will tell your main controller for the changes
YourModule.directive("batchWatch",[function(){
return {
scope:"=",
replace:false,
link:function($scope,$element,$attrs,Controller){
$scope.$watch('h',function(newVal,oldVal){
if(newVal !== oldVal){
Controller.updateChange(newVal,oldVal,$scope.$parent.$index);
}
},true);
},
controller:"yourController"
};
}]);
assume your markup is like this
<ul>
<li ng-repeat="h in complicatedArrayOfObjects">
<input type="text" ng-model="someModel" batch-watch="$index" />
</li>
</ul>
and this is your controller
YourModule.controller("yourController",[$scope,function($scope){
this.updateChange = function(newVal,oldVal,indexChanged){
console.log("Details about the change");
}
}]);
You can also play around the value provided by the directive link function which sits on first 3 arguments, scope,element and attr.

Since I didn't want another controller I ended up using ng-change instead.
Simple jsFiddle: https://jsfiddle.net/maistho/z0xazw5n/
Relevant HTML:
<body ng-app="testApp" ng-controller="testCtrl">
<div ng-repeat="foo in fooCollection">Tell me your name:
<input ng-model="foo.bar" ng-change="fooChanged(foo)">
<br />Hello, my name is {{foo.bar}}</div>
<button ng-click="fooCollection.push({})">Add a Namer</button>
</body>
Relevant JS:
angular.module('testApp', [])
.controller('testCtrl', function ($scope) {
$scope.fooCollection = [];
$scope.fooChanged = function (foo) {
console.log('foo.bar changed, new value of foo.bar is: ', foo.bar);
};
});

Try to do this
<div ng-repeat="foo in fooCollection" ng-click="select(foo)">Tell me your ame:
<input ng-model="foo.bar" ng-change="fooChanged(foo)">
<br />Hello, my name is {{foo.bar}}</div>
<button ng-click="fooCollection.push({})">Add a Namer</button>
</div>
There is the code in Directive/Controller
$scope.selectedfoo = {};
$scope.select = (foo) => {
$scope.selectedfoo = foo;
}
$scope.$watch('selectedfoo ', (newVal, oldVal) => {
if (newVal) {
}
},true)

Related

Adding ng-model directive to dynamically created input tag using AngularJs

I am trying that on a button click, a div and and input tag are created and the input tag contain ng-model and the div has binding with that input.
Kindly suggest some solution.
You can create the div and input beforehand and and do not show it by using ng-if="myVar". On click make the ng-if="true".
<button ng-click="myVar = true">
In controller : $scope.myVar = false;
$scope.addInputBox = function(){
//#myForm id of your form or container boxenter code here
$('#myForm').append('<div><input type="text" name="myfieldname" value="myvalue" ng-model="model-name" /></div>');
}
Here is another solution, in which there's no need to create a div and an input explicitly. Loop through an array of elements with ng-repeat. The advantage is that you will have all the values of the inputs in that array.
angular.module('app', [])
.controller('AppController', AppController);
AppController.$inject = ['$scope'];
function AppController($scope) {
$scope.values = [];
$scope.add = function() {
$scope.values.push('');
};
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="AppController">
<button ng-click="add()">Click</button>
<div ng-repeat="value in values track by $index">
<input type="text" ng-model="values[$index]"/>
<div>{{values[$index]}}</div>
</div>
<pre>{{values}}</pre>
</div>
UPDATE. And if you want only one input, it's even simpler, using ng-show.
angular.module('app', []);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<button ng-click="show = true">Click</button>
<div ng-show="show">
<input type="text" ng-model="value"/>
<div>{{value}}</div>
</div>
</div>
You should use $compile service to link scope and your template together:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', '$compile', '$document' , function MyCtrl($scope, $compile, $document) {
var ctrl = this;
var inputTemplate = '<div><span ng-bind="$ctrl.testModel"></span>--<span>{{$ctrl.testModel}}</span><input type="text" name="testModel"/></div>';
ctrl.addControllDynamically = addControllDynamically;
var id = 0;
function addControllDynamically() {
var name = "testModel_" + id;
var cloned = angular.element(inputTemplate.replace(/testModel/g, name)).clone();
cloned.find('input').attr("ng-model", "$ctrl." + name); //add ng-model attribute
$document.find('[ng-app]').append($compile(cloned)($scope)); //compile and append
id++;
}
return ctrl;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl as $ctrl">
<input type="button" value="Add control dynamically" ng-click="$ctrl.addControllDynamically()"/>
</div>
</div>
UPDATE: to add a new compiled template each time the button is clicked, we need to make a clone of the element.
UPDATE 2: The example above represents a dirty-way of manipulating the DOM from controller, which should be avoided. A better (angular-)way to solve the problem - is to create a directive with custom template and use it together with ng-repeat like this:
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function MyCtrl($scope) {
var ctrl = this;
ctrl.controls = [];
ctrl.addControllDynamically = addControllDynamically;
ctrl.removeControl = removeControl;
function addControllDynamically() {
//adding control to controls array
ctrl.controls.push({ type: 'text' });
}
function removeControl(i) {
//removing controls from array
ctrl.controls.splice(i, 1);
}
return ctrl;
}])
.directive('controlTemplate', [function () {
var controlTemplate = {
restrict: 'E',
scope: {
type: '<',
ngModel: '='
},
template: "<div>" +
"<div><span ng-bind='ngModel'></span><input type='type' ng-model='ngModel'/></div>" +
"</div>"
}
return controlTemplate;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.angularjs.org/1.6.2/angular.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl as $ctrl">
<input type="button" value="Add control dynamically" ng-click="$ctrl.addControllDynamically()"/>
<div ng-repeat="control in $ctrl.controls">
<control-template type="control.type" ng-model="control.value"></control-template>
</div>
</div>
</div>

Parent scope not updating from select with ng-change from directive

I've seen a couple of articles on this but still can't figure it out.
I am unable to update the parent scope from the within a directive. I have read the articles saying the scope value should not be primitive and instead it should be an object but still can't figure out why this is not working.
angular.module('moduleMihai').controller('someController',
['$scope', '$http', function ($scope, $http) {
$scope.durations = [{
field: 'yearly',
title: 'Yearly'
}, {
field: 'monthly',
title: 'Monthly'
}, {
field: 'weekly',
title: 'Weekly'
}];
$scope.selectedDuration = $scope.durations[0];
$scope.handleDurationSelection = function () {
console.log($scope.selectedDuration.field + ' selected');
$scope.someData.title[0] = "SOMETHING ELSE!!";
};
$scope.someData= {
title: ['Value1', 'Value2', 'Value3'] };
}]);
the directive doesn't have any stuff in it:
angular.module("awaCommon").directive("durationSelection", [
function () {
return {
scope: {}, // tried removing this as well as seen in some articles
restrict: "E",
templateUrl: "duration-selection.html",
link: function ($scope, $element, $attr) {
}
}
}
]);
below the duration-selection.html which contains the select:
<div ng-controller="someController">
<div>
Child: {{someData.title[0]}}
<select
ng-options="item.title for item in durations"
ng-model="selectedDuration"
ng-change="handleDurationSelection()">
</select>
</div>
So this value above in the Child: {{someData.title[0]}} - gets updated properly when value is selected. But the one in here - Parent: {{someData.title[0]}}, in the main route is not:
<div ng-controller="someController">
<div>
Parent: {{someData.title[0]}}
<duration-selection></duration-selection>
</div>
I need the parent scope to be updated in order to update different directives
The way to interact and update your parent scope from your directive is to use
event handling (emit and broadcast) Todd about events $emit and $broadcast : so here we alert the parent when there is a change from the child directive, then the parent listens for the event. I suggest minimal usage due to some bad sides
directive attribute to pass the function: we pass our function to be processed to our directive to handle or call it when needed from the directive ( for me the best method)
inside the directive to update the $scope.$parent.lngBusinessUnit, no need to pass the function to the directive again, not necessary. since the directive is the one handling the logic. we just directly update the parent straight up.
the use of $watch on the parent directive to help check for changes of the selectedDuration $watch read more: this is quite easy since we map the ngModel to the passed param of our directive using two way binding in our return->scope "=" from directive setup
Example For each of the above possibilities
Event Handling
angular.module("eventTest", [])
.controller("mainCtrl", function ($scope){
console.log("am here");
$scope.parentValue = "test";
$scope.valueToPass = ["Male", "Female"];
//let's catch the updated content
$scope.$on('childUpdated', function(event, value){
$scope.parentValue = value;
console.log("updated from child directive", value);
});
})
.directive("child", function(){
return {
restrict:'E',
scope: {
valueToPass:"="
},
templateUrl:"child.html",
controller: function ($scope){
//this is method is triggered when the select of our valueToPass is changed
$scope.childChanges = function (value){
$scope.$emit('childUpdated', value);
console.log("child emitted this:", value);
}
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="eventTest">
<div ng-controller="mainCtrl">
<h1>Event Test Just for your case, advise you read up</h1>
Parent: <b>{{parentValue}}</b>
<br>
<child value-to-pass="valueToPass"></child>
</div>
<script type='text/ng-template' id="child.html">
Child value : <b>{{menu}}<b> <br>
<select ng-model="menu" ng-change="childChanges(menu)">
<option ng-repeat="item in valueToPass">{{item}}</option>
</select>
</script>
</body>
directive attribute , using function
angular.module("eventTest", [])
.controller("mainCtrl", function ($scope){
console.log("am here");
$scope.parentValue = "test";
$scope.primaryVariable = "Male";
$scope.onChange = function (){
$scope.parentValue = $scope.primaryVariable;
}
})
.directive("child", function(){
return {
restrict:'E',
scope: {
primaryVariable:"=",
callMe:"&"//note this syntax, check angular directive doc
},
templateUrl:"child.html",
controller: function ($scope){
$scope.valueToPass = ["Male", "Female"];
//this is method is triggered when the select of our primaryVarible is changed
$scope.childChanges = function (){
$scope.callMe();
}
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="eventTest">
<div ng-controller="mainCtrl">
<h1>Directive Function Passing</h1>
Parent: <b>{{parentValue}}</b>
<br>
<child primary-variable="primaryVariable" call-me="onChange()"></child>
</div>
<script type='text/ng-template' id="child.html">
Child value : <b>{{primaryVariable}}<b> <br>
<select ng-model="primaryVariable" ng-change="childChanges()">
<option ng-repeat="item in valueToPass">{{item}}</option>
</select>
</script>
</body>
using scope.$parent
angular.module("eventTest", [])
.controller("mainCtrl", function ($scope){
console.log("am here");
$scope.parentValue = "test";
$scope.primaryVariable = "Male";
})
.directive("child", function(){
return {
restrict:'E',
scope: {
primaryVariable:"="
},
templateUrl:"child.html",
controller: function ($scope){
$scope.valueToPass = ["Male", "Female"];
//this is method is triggered when the select of our primaryVarible is changed
$scope.childChanges = function (){
$scope.$parent.parentValue = $scope.primaryVariable;
}
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="eventTest">
<div ng-controller="mainCtrl">
<h1>Using $parent</h1>
Parent: <b>{{parentValue}}</b>
<br>
<child primary-variable="primaryVariable"></child>
</div>
<script type='text/ng-template' id="child.html">
Child value : <b>{{primaryVariable}}<b> <br>
<select ng-model="primaryVariable" ng-change="childChanges()">
<option ng-repeat="item in valueToPass">{{item}}</option>
</select>
</script>
</body>
Using the $watch
angular.module("eventTest", [])
.controller("mainCtrl", function ($scope){
console.log("am here");
$scope.parentValue = "test";
$scope.primaryVariable = "Male";
$scope.$watch('primaryVariable', function(){
$scope.parentValue = $scope.primaryVariable;
});
})
.directive("child", function(){
return {
restrict:'E',
scope: {
primaryVariable:"="
},
templateUrl:"child.html",
controller: function ($scope){
$scope.valueToPass = ["Male", "Female"];
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="eventTest">
<div ng-controller="mainCtrl">
<h1>using $watch</h1>
Parent: <b>{{parentValue}}</b>
<br>
<child primary-variable="primaryVariable"></child>
</div>
<script type='text/ng-template' id="child.html">
Child value : <b>{{primaryVariable}}<b> <br>
<select ng-model="primaryVariable" ng-change="childChanges()">
<option ng-repeat="item in valueToPass">{{item}}</option>
</select>
</script>
</body>
Hope this helps

angular1.3.7 ng-repeat binding was not expected

I'm looking for an explanation of how angular binding works behind the curtain. I only want $scope.values to be updated on ng-click, but once its clicked once it bound to the form forever. woah. Why?
First example is NOT working as I expected it to work. The values should only be reflected in the span everytime I click on my button.
http://jsfiddle.net/webmandman/Ls4g4yLn/12/
html:
<div ng-app="app" ng-controller="myCtrl">
<div>
<span ng-repeat="value in values">{{value.text}}</span><br>
<input ng-repeat="field in fields" type="text" name="{{field.name}}" ng-model="field.text"/>
</div>
<button ng-click="display()">display</button>
</div>
controller:
var app = angular.module('app', []);
angular.module('app').controller("myCtrl", function($log,$scope, $http) {
$scope.fields = [
{name:"Name", text:null},
{name:"Phone", text:null}
];
$scope.display = function(){
$log.log("display has been called...");
$scope.values = $scope.getValues()
};
$scope.getValues = function(){
$log.log('getValues was called.');
var list = [];
angular.forEach($scope.fields, function(value, index){
//$log.log(item);
$log.log(value);
$log.log(index);
this.push(value);
//this.push(item);
},list);
return list;
};
});
This example is working like I want it too, but it is not using ng-repeat.
http://jsfiddle.net/webmandman/Ls4g4yLn/13/
html:
<div ng-app="app" ng-controller="myCtrl">
<div>
<span>{{values.join(',')}}</span><br>
<input ng-repeat="field in fields" type="text" name="{{field.name}}" ng-model="field.text"/>
</div>
<button ng-click="display()">display</button>
</div>
controller:
var app = angular.module('app',[]);
angular.module('app').controller("myCtrl", function($log,$scope, $http) {
$scope.fields = [
{name:"Name", text:null},
{name:"Phone", text:null}
];
$scope.display = function(){
$log.log("display has been called...");
$scope.values = $scope.getValues()
};
$scope.getValues = function(){
var list = [];
angular.forEach($scope.fields, function(value, index){
//$log.log(item);
$log.log(value);
$log.log(index);
this.push(value.text);
//this.push(item);
},list);
return list;
};
});
That's because in your $scope.values, you push refercnes to your $scope.fields, hence then when you edit a field text, it is rendered in both places.
Instead, you can simply push the text in your values, as String are passed by copy and not reference (that's pure Javascript not related to Angular).
Here is a working fiddle:
http://jsfiddle.net/zrwq6cmu/

Bind search input to ng-repeat in different controller

I am building a web application using Angular. We have a Twitter-like navigation bar up top with a search box in it. Then we have a bunch of entries below, using the ng-repeat directive. I want to be able to bind the search box with the entries below. The challenge is due to the fact that our header and our entries are in two different controllers. If they were in the same controller, then we could do this:
<input type="search" ng-model="search">
<div ng-repeat="entry in entries | filter:search">
{{ entry.text }}
</div>
But since in my application the search box is in a different controller, search isn't in scope so it's not working.
Any suggestions?
If you put the search string inside a service you can share the data between both controllers.
Here is an example.
<!doctype html>
<html ng-app="app">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.js"></script>
<script>
angular.module('app', []);
angular.module('app')
.controller('Ctrl1', function($scope, ShareData) {
$scope.myData1 = ShareData;
});
angular.module('app')
.controller('Ctrl2', function($scope, ShareData) {
$scope.myData2 = ShareData;
$scope.entries = [
'1',
'2',
'3',
'11'
]
});
angular.module('app')
.service('ShareData', function(){
return {
search: "1"
}
})
</script>
</head>
<body >
<div ng-controller="Ctrl1">
<h2>Inside Ctrl 1</h2>
<input type="text" ng-model="myData1.search">
</div>
<hr>
<div ng-controller="Ctrl2">
<h2>Inside Ctrl 2</h2>
<div ng-repeat="entry in entries | filter:myData2.search">
{{entry}}
</div>
</div>
</body>
</html>
You can do one thing use $emit on rootscope and capture it in another controller:-
For example:-
<input type="search" ng-model="search">
Controller one:-
$scope.$watch('search',function(new){
$rootScope.$emit('update',new);
});
Controller Second:-
$rootScope.$on('update', function (event, data) {
$scope.search=data;
});
Secondly you can also share data from controller's via service (this one is effective)
myApp.factory('Data', function () {
var data = {
search: ''
};
return {
getSearch: function () {
return data.search;
},
setSearch: function (search) {
data.search= firstName;
}
};
});
myApp.controller('FirstCtrl', function ($scope, Data) {
$scope.firstName = '';
$scope.$watch('search', function (newValue) {
if (newValue) Data.setSearch(newValue);
});
});
myApp.controller('SecondCtrl', function ($scope, Data) {
$scope.$watch(function () { return Data.getSearch(); }, function (newValue) {
if (newValue) $scope.search = newValue;
});
});

Access child scope corresponding to a model variable

I have a list of items and I've rendered them in a template via ng-repeat. Each of them is controlled by an itemController which exposes some behavior for item (e.g. grabing focus). Here is the HTML:
<body ng-controller="mainController">
<button ng-click="addItem()">add</button>
<ul>
<li ng-repeat="item in items" ng-controller="itemController">
<div ng-if="item.isEditing">
<input ng-model="item.name"/>
<button ng-click="item.isEditing=false">done</button>
</div>
<span ng-if="!item.isEditing">{{item.name}}</span>
</li>
</ul>
</body>
In the mainController, I have a function for adding a new item to items. Here is the code for mainController:
app.controller('mainController', function($scope){
$scope.items = [
{
name: "alireza"
},
{
name: "ali"
}
];
$scope.addItem = function(){
$scope.items.push({isEditing: true});
}
});
Whenever I add an item to items array, a corresponding li element is added into the view which is controlled by an instance of itemController, and the corresponding model is the new item I've just added (or maybe the scope of the itemController, which contains item).
Problem:
When I add some item to items, I only have access to item and not the scope of the recently created item. So I can't run some function (like grabFocus) on the scope of new item.
Is it something semantically wrong in my design? What is the canonical approach for this problem?
Plunker link
Here is the plunker link with related comments
You can use $broadcast from the parent scope, along with $on from the child scope, to notify the child scopes of the newly added item. And by passing (as an argument) the $id of the child scope that corresponds to the newly added item, each child catching the event can know whether or not it's the one that needs to have grabFocus() called.
Here's a fork of your Plunker that uses that approach. I wasn't sure what you were trying to accomplish with $element.find(":text").focus(); in your original Plunker, so I tweaked it to toggle a $scope property that in turn controlled a style in the view. The newly added item will be red (by calling its own grabFocus function to toggle the flag to true), and the others will be black (by calling their own loseFocus function to toggle the flag to false).
Modified HTML (just the repeated li):
<li ng-repeat="item in items" ng-controller="itemController">
<div ng-if="item.isEditing">
<input ng-model="item.name"/>
<button ng-click="item.isEditing=false;handleItemAdded($index);">done</button>
</div>
<span ng-if="!item.isEditing" ng-style="{ color: isFocused ? 'red' : 'black' }">{{item.name}}</span>
</li>
Full JavaScript:
var app = angular.module("app",[]);
app.controller('mainController', function($rootScope, $scope){
$scope.items = [ { name: "alireza" }, { name: "ali" } ];
$scope.addItem = function(){
$scope.items.push({isEditing: true});
};
$scope.handleItemAdded = function (index) {
// $rootScope.$broadcast('item-added', { index: index });
for(var cs = $scope.$$childHead; cs; cs = cs.$$nextSibling) {
if (cs.$index === index) {
$rootScope.$broadcast('item-added', { id: cs.$id });
break;
}
}
};
});
app.controller('itemController', function($scope, $element){
$scope.$on('item-added', function (event, args) {
if ($scope.$id === args.id + 1) {
$scope.grabFocus();
} else {
$scope.loseFocus();
}
});
$scope.grabFocus = function() {
$scope.isFocused = true;
};
$scope.loseFocus = function() {
$scope.isFocused = false;
};
});
I changed your approach a little bit by creating an unique id for every input, based on its index number. See code below, hope it helps.
// Code goes here
var app = angular.module("app",[]);
app.controller('mainController', function($scope,$timeout){
$scope.items = [
{
name: "alireza"
},
{
name: "ali"
}
];
$scope.addItem = function(){
$scope.items.push({isEditing: true});
$timeout(function(){
document.getElementById("newItem"+($scope.items.length-1)).focus();
},0)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="app">
<head>
<script data-require="angular.js#1.3.0" data-semver="1.3.0" src="//code.angularjs.org/1.3.0/angular.js"></script>
<link href="style.css" rel="stylesheet" />
<script src="script.js"></script>
</head>
<body ng-controller="mainController">
<button ng-click="addItem()">add</button>
<ul>
<li ng-repeat="item in items">
<div ng-if="item.isEditing">
<input ng-model="item.name" id="newItem{{$index}}"/>
<button ng-click="item.isEditing=false">done</button>
</div>
<span ng-if="!item.isEditing">{{item.name}}</span>
</li>
</ul>
</body>
</html>

Resources