I'm trying to perform update on a value but ,the new value that am assigning to it bound to scope value but does not change ,when I edit ,it save with the original empty value
breakdown
$scope.showDepositUpdate = function(birthday) {
$scope.depositValue="one";
$scope.birthday = birthday;
$scope.action = 'deposit';
$scope.isAdd = false;
$scope.showUpdateModal = true;
};
$scope.updateDeposit = function() {
$scope.birthday.Name = $scope.depositValue;
StoreFactory.updateBirthday($scope.birthday);
$scope.depositValue='';
newValue =""
$scope.showUpdateModal = false;
};
but scope.depositValue does not update it according to value on the view , it always concat to "", here is my view
<form class="form-inline" ng-show="showUpdateModal">
<h2 class="title">{{ action }} Birthday</h2>
<div class="form-group">
<input type="text" class="form-control" ng-model="birthday.Name" placeholder="name" placeholder="Jane Doe">
</div>
<div class="form-group">
<input type="text" class="form-control" ng-model="depositvalue" placeholder="name" placeholder="deposit">
</div>
<button ng-click="updateDeposit()" class="btn btn-default btn-lg btn-rounded">Save</button>
</form>
<tr ng-repeat="birthday in birthdays">
<td>{{ birthday.Name }}</td>
<td>{{ birthday.Date }}</td>
<td> <button class="btn btn-info btn-sm btn-rounded" ng-click="showDepositUpdate(birthday)">deposit</button>
In your function $scope.updateDeposit you are stting up the value of the variable depositValue to "". $scope.depositValue='';. Maybe this is your problem?
I've done a snipet to show you that.
See if it is what you want.
var $scope = {};
var myApp = angular.module('myApp', []);
var StoreFactory = {
updateBirthday: function(){return true} // JUST A MOCKUP
};
myApp.controller('myCtrl', ['$scope',
function($scope) {
$scope.showDepositUpdate = function(birthday) {
$scope.depositValue = "one";
$scope.birthday = birthday;
$scope.action = 'deposit';
$scope.isAdd = false;
$scope.showUpdateModal = true;
};
$scope.updateDeposit = function() {
$scope.birthday.Name = $scope.depositValue;
StoreFactory.updateBirthday($scope.birthday);
$scope.depositValue = '';
newValue = ""
$scope.showUpdateModal = false;
};
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<form class="form-inline" ng-show="showUpdateModal">
<h2 class="title">{{ action }} Birthday</h2>
<div class="form-group">
<input type="text" class="form-control" ng-model="birthday.Name" placeholder="Jane Doe">
</div>
<div class="form-group">
<input type="text" class="form-control" ng-model="depositvalue" placeholder="deposit">
</div>
<button ng-click="updateDeposit()" class="btn btn-default btn-lg btn-rounded">Save</button>
</form>
<tr ng-repeat="birthday in birthdays">
<td>{{ birthday.Name }}</td>
<td>{{ depositvalue }}</td>
<td>
<button class="btn btn-info btn-sm btn-rounded" ng-click="showDepositUpdate(birthday)">deposit</button>
</td>
</tr>
Did you try a console.log() of the value in your Factory? Whats the value there?
I once had such a problem and that was because (referred to your problem)
$scope.birthday.Name
is just a reference to $scope.depositValue and so you pass a reference to a function that is changed quite after function call ($scope.depositValue = '').
Would be good to know what StoreFactory.updateBirthday($scope.birthday); actually does.
Related
I'm trying to make a simple form for insert, delete, update using localStorage to store my data. When I click Add button, It shows an error
TypeError: $scope.lstLaptop.push is not a function.
I was back to my code and check syntax if I'm wrong, but I think my code was only 3 lines and look usual. Can you tell me what I was missing something or what the problem really was from?
Just ignored my other code and check my controller lapCreateUpdateCtrl please, I'm out of idea what I'm wrong.
HTML file:
<div class="container">
<table class="table table-hover">
<tr>
<th>Laptop Model</th>
<th>Price($)</th>
<th>Option</th>
</tr>
<tr ng-repeat = "laptops in lstLaptop track by $index">
<td><p ng-bind = laptops.model></p></td>
<td><p ng-bind = laptops.price></p></td>
<td><button type="button" ng-click="remove1($index)"
class="btn btn-danger btn-xs">
Delete
</button>
<button type="button" ng-click="edit1($index)"
class="btn btn-warning btn-xs">
Edit
</button>
<button type="button" ng-click="update1($index)"
class="btn btn-info btn-xs">
Update
</button>
</td>
</tr>
</table>
<button type="button" class="btn btn-success btn-sm"
ng-click="save()">
Save
</button>
</div>
</div>
</body>
app.JS file:
routerApp.controller('lapCreateUpdateCtrl', ["$scope", function($scope){
$scope.laptop = {};
$scope.lstLaptop = [];
function init(){
var strLaptop = window.localStorage.getItem("LAPTOP_KEY");
if(strLaptop){
$scope.lstLaptop = JSON.parse(strLaptop);
}
}
init();
$scope.add1 = function(){
$scope.lstLaptop.push($scope.laptop);
$scope.laptop = {};
}
$scope.remove1 = function(index){
$scope.lstLaptop.splice(index,1);
alert("Deleted!");
}
$scope.edit1 = function(index){
$scope.laptop = angular.copy($scope.lstLaptop[index]);
}
$scope.update1 = function(index){
$scope.lstLaptop.splice(index, 1, $scope.laptop);
$scope.laptop = {};
}
$scope.save=function(){
window.localStorage.setItem("LAPTOP_KEY", JSON.stringify($scope.lstLaptop));
}
}]);
I want content input from textbox
<input type="text" ng-model="laptop.model" id="model" name="model"
placeholder="Model" required />
<input type="number" ng-model="laptop.price" id="price" name="price"
placeholder="Price" required />
<button type="button" ng-click="add()">
Add Desktop
</button>
You have called init() and then inside that function, you have changed the $scope.lstLaptop to an object. When $scope.add1 will be called, it will throw an error as object does not have push function. It only works with Arrays.
I don't understand what you want to achieve, but you can do something like below. It will retain the lstLaptop as array
function init(){
var strLaptop = window.localStorage.getItem("LAPTOP_KEY");
if(strLaptop){
$scope.lstLaptop.push(JSON.parse(strLaptop));
}
}
I am making a form to create orders. The input fields are dynamic and can be added with a button.
Now after every row there is a delete button, which should delete the row.
For this this I made the delRow() function. It works because the row gets deleted, unfortunately the values of the rows underneath it are removed as well, but the input fields stay in place. I can't seem to figure out why.
Template:
<form>
<div class="form">
<table>
<tr ng-repeat="row in rows">
<td>
<input class="product" ng-model="row.product[$index]" placeholder="{{productPlaceholder}}" type="text">
</td>
<td>
<input ng-model="row.amount[$index]" type="number" min="0">
</td>
<td>
<button class="btn btn-primary btn-functionality btn-danger btn-del" ng-click="delRow($index)">x</input>
</td>
</tr>
</table>
<button class="btn btn-primary btn-success btn-add" ng-click="addRow()">+</button>
</div>
<div class="main-buttons">
<button class="btn btn-primary btn-lg btn-create" ng-click="createOrder()">Create</button>
<button class="btn btn-primary btn-lg" ng-click="cancelOrder()">Cancel</button>
</div>
</form>
Controller:
'use strict';
angular.module('myApp.orderNew', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/order/new', {
templateUrl: 'order-new/order-new.template.html',
controller: 'OrderNewCtrl'
});
}])
.controller('OrderNewCtrl', function($scope, $location, $http, $log) {
$scope.$log = $log;
$scope.message ="test";
$scope.rows = [{}];
$scope.counter = 1;
$scope.productPlaceholder = 'Product';
$scope.addRow = function() {
$scope.rows.push({});
$scope.counter++;
}
$scope.delRow = function(index) {
if ($scope.rows.length < 2) { return; }
$scope.rows.splice(index, 1);
}
$scope.cancelOrder = function() {
$location.path('/orders');
}
$scope.createOrder = function() {
var data = $scope.fields;
alert(data);
//$post('/path_to_server', obj);
}
$http.get('orders.json').then(function(data) {
$scope.orders = data;
});
});
Instead of:
<input ng-model="row.product[$index]" type="number" min="0">
You should do this:
<input ng-model="row.product" type="number" min="0">
Because you are deleting the rows the index reference is getting thrown off. Instead of using a index reference you can just just place the values directly to a attribute of the object.
I whipped up a working pen here:
http://codepen.io/nilestanner/pen/gmyBRo?editors=1011
I want to retain the selected check boxes as is even when I am
changing my search query. Initially I am posting some query in search
and selecting one of the resulted values, Now if I change my search
query, then New values will be my result. But I want to retain the
checkbox selected for the previous values...
`
//Demo of Searching and Sorting Table with AngularJS
var myApp = angular.module('myApp',[]);
myApp.controller('TableCtrl', ['$scope', function($scope) {
$scope.allItems = getDummyData();
$scope.resetAll = function()
{
$scope.filteredList = $scope.allItems ;
$scope.newEmpId = '';
$scope.newName = '';
$scope.newEmail = '';
$scope.searchText = '';
}
$scope.add = function()
{
$scope.allItems.push({EmpId : $scope.newEmpId, name : $scope.newName, Email:$scope.newEmail});
$scope.resetAll();
}
$scope.search = function()
{
$scope.filteredList = _.filter($scope.allItems,
function(item){
return searchUtil(item,$scope.searchText);
});
if($scope.searchText == '')
{
$scope.filteredList = $scope.allItems ;
}
}
$scope.resetAll();
}]);
/* Search Text in all 3 fields */
function searchUtil(item,toSearch)
{
/* Search Text in all 3 fields */
return ( item.name.toLowerCase().indexOf(toSearch.toLowerCase()) > -1 || item.Email.toLowerCase().indexOf(toSearch.toLowerCase()) > -1 || item.EmpId == toSearch
)
? true : false ;
}
/*Get Dummy Data for Example*/
function getDummyData()
{
return [
{EmpId:2, name:'Jitendra', Email: 'jz#gmail.com'},
{EmpId:1, name:'Minal', Email: 'amz#gmail.com'},
{EmpId:3, name:'Rudra', Email: 'ruz#gmail.com'}
];
}
.icon-search{margin-left:-25px;}
<br /> <br />
<div ng-app="myApp">
<div ng-controller="TableCtrl">
<div class="input-group">
<input class="form-control" ng-model="searchText" placeholder="Search" type="search" ng-change="search()" />
<span class="input-group-addon">
<span class="glyphicon glyphicon-search"></span>
</span>
</div>
<table class="table table-hover data-table sort display">
<thead>
<tr>
<th class="EmpId"> <a href="" ng-click="columnToOrder='EmpId';reverse=!reverse">EmpId
</a></th>
<th class="name"> Name </th>
<th class="Email"> Email </th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in filteredList | orderBy:columnToOrder:reverse">
<td><input type="checkbox" name="test" />{{item.EmpId}}</td>
<td>{{item.name}}</td>
<td>{{item.Email}}</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-xs-3">
<input type="text" ng-model="newEmpId" class="form-control" placeholder="EmpId">
</div>
<div class="col-xs-3">
<input type="text" ng-model="newName" class="form-control" placeholder="Name">
</div>
<div class="col-xs-4">
<input type="email" ng-model="newEmail" class="form-control" placeholder="Email">
</div>
<div class="col-xs-1">
<button ng-click="add()" type="button" class="btn btn-primary">
<span class="glyphicon glyphicon-plus"></span>
</button>
</div>
</div>
</div> <!-- Ends Controller -->
</div>
`Fiddle
Try to add ng-model="item.selected" to your checkbox tag
<td><input ng-model="item.selected" type="checkbox" name="test" />{{item.EmpId}}</td>
Works for me, hope it helps.
Looks like this is happening because you are resetting the items here:
if($scope.searchText == '')
{
$scope.filteredList = $scope.allItems ;
}
and allItems doesn't tell anywhere if the checkbox needs to be selected on not. I would suggest you to update the code where you are creating the checkboxes, something like:
<td><input type="checkbox" name="test" ng-model=item.selected ng-checked=item.selected/>
Note that I have updated the item to have a 'selected' field which will tell if that item is selected or not(default could be false). While creating the checkbox I have linked the model using ng-model=item.selected
Updated fiddle at http://jsfiddle.net/3a3zD/194/
I'm getting this error when I open up a model partial:
<form action="" novalidate name="newGroupForm">
<div class="modal-body">
<div class="row">
<!-- SELECT THE NUMBER OF GROUPS YOU WANT TO CREATE -->
<label>Select number of groups</label>
<a class="btn" ng-click="newGroupCount = newGroupCount + 1" ng-disabled="newGroupCount == 10" ><i class="fa fa-plus-circle"></i></a>
<input class="groupCounter input-sm" ng-model="newGroupCount" type="number" min="1" max="10" disabled>
<a class="btn" ng-click="newGroupCount = newGroupCount - 1" ng-disabled="newGroupCount == 1"><i class="fa fa-minus-circle"></i></a>
</div>
<br>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Group Name</th>
<th>Group Description (optional)</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="i in getNumber(newGroupCount) track by $index">
<td>{{$index+1}}</td>
<td>
<input class= input-sm type="text" required="true" autofocus="true" placeholder="Group name" ng-model="groupData.title[$index]">
</td>
<td>
<input class="form-control input-sm" type="textarea" ng-model="groupData.desc[$index]" placeholder="Group Description">
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
<button class="btn btn-primary" type="submit" ng-click="submit()" ng-disabled="newGroupForm.$invalid">Create</button>
</div>
</form>
The modal controller looks like this:
spApp.controller('newGroupCtrl',
function newGroupCtrl($scope, $uibModalInstance, GroupService){
$scope.groupData = {
title: [],
desc: []
}
$scope.newGroupCount = 1;
$scope.getNumber = function(num) {
//console.log(num);
return new Array(num);
}
$scope.submit = function(){
$uibModalInstance.close($scope.groupData);
}
$scope.cancel = function (){
$uibModalInstance.dismiss('Cancelled group creation');
};
}
);
Every question I've seen refers to the use of filterbut I'm not using filter. The error repeats whenever I hit the increment button:
<a class="btn" ng-click="newGroupCount = newGroupCount + 1" ng-disabled="newGroupCount == 10" ><i class="fa fa-plus-circle"></i></a>
$scope.getNumber calls new Array(num), which will return an array of undefined values directly proportional to the value of newGroupCount.
For example:
new Array(5) // => [undefined, undefined, undefined, undefined, undefined]
Browsers don't handle that well, since it appears to be an empty array.
You're using ng-repeat in a way that it wasn't quite meant to be used. If I were you, I'd refactor to look something like this:
$scope.groups = [];
$scope.addGroup = function() {
// implement this, and use it in your button that increments the groups
$scope.groups.push(/* whatever */);
}
$scope.removeGroup = function() {
// implement this to remove a group
$scope.groups.splice(/* whatever */);
}
Then in your HTML:
<tr ng-repeat="group in groups">
<!-- display group info -->
</tr>
It may make your life easier here to work with angular (use it how it was intended) instead of fighting against how ng-repeat is meant to work.
The data is generally meant to be in the form of a collection (i.e. [{},{},{}]). Formatting it as such will make it easier for you. See the docs for ng-repeat.
I have a problem with an AngularJS app I'm making. It shows a list of contacts, and for each contact there is a button whereupon clicking the button a modal pops up with a form. This form should show the existing contact information, and if you want to change something you type the new information and press submit.
The problem, however, is that the existing information is not shown in the form, hence editing it doesn't work. I imagine that the issue is that the modal does not inherit the scope from the parent page, but I don't know what to do in order to fix that. I have tried playing around with the attributes on the input fields (for example prepending ng-model by $parent. and defining an ng-init value), but to no avail, so I hope some of the experts here will be able to point me on the right track.
Thank you in advance.
Now let me show you my code, so you can see the context I'm talking about. Here is the html that displays the list of contacts:
<div class="panel panel-default" ng-controller="contactsController">
<div class="panel-body">
<div id="gridContainer" ng-class="{'': state == 'list', 'none': state != 'list'}">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th scope="col"><spring:message code="contacts.name"/></th>
<th scope="col"><spring:message code="contacts.email"/></th>
<th scope="col"><spring:message code="contacts.phone"/></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="contact in page.source">
<td class="tdContactsCentered">{{contact.name}}</td>
<td class="tdContactsCentered">{{contact.email}}</td>
<td class="tdContactsCentered">{{contact.phoneNumber}}</td>
<td class="width15">
<div class="text-center">
<input type="hidden" value="{{contact.id}}"/>
<a ng-href="#updateContactsModal"
ng-click="selectedContact(contact);"
role="button"
title="<spring:message code="update"/> <spring:message code="contact"/>"
class="btn btn-sm btn-warning" data-toggle="modal">
<i class="icon-pencil"></i>
</a>
<a ng-href="#deleteContactsModal"
ng-click="selectedContact(contact);"
role="button"
title="<spring:message code="delete"/> <spring:message code="contact"/>"
class="btn btn-sm btn-danger" data-toggle="modal">
<em class="fa fa-trash"></em>
</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
And the html that defines the modal and the form:
<div id="updateContactsModal"
class="modal fade centering"
role="dialog"
aria-labelledby="updateContactsModalLabel"
aria-hidden="true" style="display: none;">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h3 id="updateContactsModalLabel" class="modal-title">
<spring:message code="update"/> <spring:message code="contact"/>
</h3>
</div>
<div class="modal-body" data-ng-controller="contactsController">
<form name="updateContactForm" novalidate>
<input type="hidden"
required
data-ng-model="contact.id"
name="id"
value="{{contact.id}}"/>
<div>
<div class="form-group">
<label>* <spring:message code="contacts.name"/>:</label>
<input type="text"
autofocus
required
class="form-control"
data-ng-model="contact.name"
name="name"
placeholder="<spring:message code='contact'/> <spring:message code='contacts.name'/> "/>
<div>
<span class="alert alert-error"
ng-show="displayValidationError && updateContactForm.name.$error.required">
<spring:message code="required"/>
</span>
</div>
</div>
<div class="form-group">
<label>* <spring:message code="contacts.email"/>:</label>
<div class="input-append">
<input type="text"
required
class="form-control"
ng-model="contact.email"
name="email"
placeholder="<spring:message code='sample.email'/> "/>
</div>
<div>
<span class="alert alert-error"
ng-show="displayValidationError && updateContactForm.email.$error.required">
<spring:message code="required"/>
</span>
</div>
</div>
<div class="form-group">
<label>* <spring:message code="contacts.phone"/>:</label>
<div class="input-append">
<input type="text"
required
class="form-control"
ng-model="contact.phoneNumber"
name="phoneNumber"
placeholder="<spring:message code='sample.phone'/> "/>
</div>
<div>
<span class="alert alert-error"
ng-show="displayValidationError && updateContactForm.phoneNumber.$error.required">
<spring:message code="required"/>
</span>
</div>
</div>
</div>
</form>
<div class="modal-footer">
<input type="submit"
class="btn btn-primary"
ng-click="updateContact(updateContactForm);"
value='<spring:message code="update"/>'/>
<button class="btn btn-default"
data-dismiss="modal"
ng-click="exit('#updateContactsModal');"
aria-hidden="true">
<spring:message code="cancel"/></button>
</div>
</div>
<span class="alert alert-error dialogErrorMessage"
ng-show="errorOnSubmit">
<spring:message code="request.error"/>
</span>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
And finally the relevant parts of the controller:
App.controller('contactsController', ["$scope", "$http", function($scope,$http) {
$scope.pageToGet = 0;
$scope.state = 'busy';
$scope.lastAction = '';
$scope.url = "/uaiContacts/protected/contacts/";
$scope.errorOnSubmit = true;
$scope.errorIllegalAccess = false;
$scope.displayMessageToUser = false;
$scope.displayValidationError = false;
$scope.displaySearchMessage = false;
$scope.displaySearchButton = false;
$scope.displayCreateContactButton = false;
$scope.contact = {};
$scope.searchFor = "";
$scope.getContactList = function () {
var url = $scope.url;
$scope.lastAction = 'list';
$scope.startDialogAjaxRequest();
var config = {params: {page: $scope.pageToGet}};
$http.get(url, config)
.success(function (data) {
$scope.finishAjaxCallOnSuccess(data, null, false);
})
.error(function () {
$scope.state = 'error';
$scope.displayCreateContactButton = false;
});
};
$scope.populateTable = function (data) {
if (data.pagesCount > 0) {
$scope.state = 'list';
$scope.page = {source: data.contacts, currentPage: $scope.pageToGet, pagesCount: data.pagesCount, totalContacts : data.totalContacts};
if($scope.page.pagesCount <= $scope.page.currentPage){
$scope.pageToGet = $scope.page.pagesCount - 1;
$scope.page.currentPage = $scope.page.pagesCount - 1;
}
$scope.displayCreateContactButton = true;
$scope.displaySearchButton = true;
} else {
$scope.state = 'noresult';
$scope.displayCreateContactButton = true;
if(!$scope.searchFor){
$scope.displaySearchButton = false;
}
}
if (data.actionMessage || data.searchMessage) {
$scope.displayMessageToUser = $scope.lastAction != 'search';
$scope.page.actionMessage = data.actionMessage;
$scope.page.searchMessage = data.searchMessage;
} else {
$scope.displayMessageToUser = false;
}
};
$scope.exit = function (modalId) {
$(modalId).modal('hide');
$scope.contact = {};
$scope.errorOnSubmit = false;
$scope.errorIllegalAccess = false;
$scope.displayValidationError = false;
};
$scope.finishAjaxCallOnSuccess = function (data, modalId, isPagination) {
$scope.populateTable(data);
$("#loadingModal").modal('hide');
if(!isPagination){
if(modalId){
$scope.exit(modalId);
}
}
$scope.lastAction = '';
};
$scope.startDialogAjaxRequest = function () {
$scope.displayValidationError = false;
$("#loadingModal").modal('show');
$scope.previousState = $scope.state;
$scope.state = 'busy';
};
$scope.handleErrorInDialogs = function (status) {
$("#loadingModal").modal('hide');
$scope.state = $scope.previousState;
// illegal access
if(status == 403){
$scope.errorIllegalAccess = true;
return;
}
$scope.errorOnSubmit = true;
$scope.lastAction = '';
};
$scope.addSearchParametersIfNeeded = function(config, isPagination) {
if(!config.params){
config.params = {};
}
config.params.page = $scope.pageToGet;
if($scope.searchFor){
config.params.searchFor = $scope.searchFor;
}
};
$scope.selectedContact = function (contact) {
$scope.contact = angular.copy(contact);
debugger;
};
$scope.updateContact = function (updateContactForm) {
if (!updateContactForm.$valid) {
debugger;
$scope.displayValidationError = true;
return;
}
$scope.lastAction = 'update';
var url = $scope.url + $scope.contact.id;
$scope.startDialogAjaxRequest();
var config = {};
$scope.addSearchParametersIfNeeded(config, false);
$http.put(url, $scope.contact, config)
.success(function (data) {
$scope.finishAjaxCallOnSuccess(data, "#updateContactsModal", false);
})
.error(function(data, status, headers, config) {
$scope.handleErrorInDialogs(status);
});
};
$scope.getContactList();
}]);
The modal doesn't share the same scope as the contacts table because each time Angular finds another ng-controller directive, it creates a new scope.
You're declaring ng-scope in both the contacts table and the modal, which causes angular to create different scopes.
See this answer for more details:
https://stackoverflow.com/a/14462341/4938335
There are a few ways to solve this...
1) Put the modal HTML inside the parent element where you're already declaring ng-controller the first time - that way it will be part of the same scope
2) Use UI Bootstrap's modal directive to generate the modal with its own controller and pass in $scope.contact from your contactsController. See example here https://angular-ui.github.io/bootstrap/#/modal
3) Create a service that stores $scope.contact and inject that into a separate controller that you create for the modal. Here's someone else's fiddle that shows this:
http://jsfiddle.net/whnSs/