I'm new to bootstrap. I want to do CRUD operations on the employee data from asp.net WebAPI using Bootstrap-Modal. I'm able to display the data from webapi to angularJS using a table(ng-repeat). In every row, at the end, I've three buttons VIEW, DELETE and EDIT and another outside the table ADD.
I want, whenever a user clicks on the ADD, VIEW, DELETE and EDIT button, a bootstrap modal should pop up and we should be able to perform CRUD operations.
I've tried many instances but no luck on how to get the data on Modal. Please help.
The code is as follows:
WEBAPI:
public class EmployeeService
{
SampleDatabaseEntities sampleDBEntities = new SampleDatabaseEntities();
//ADD USER
public int saveEmployee(EmployeeTable employeeTable)
{
sampleDBEntities.EmployeeTables.Add(employeeTable);
sampleDBEntities.SaveChanges();
return employeeTable.E_ID;
}
public EmployeeTable getEmployeeById(int id)
{
return sampleDBEntities.EmployeeTables.Find(id);
}
public List<EmployeeTable> getEmployees()
{
return sampleDBEntities.EmployeeTables.ToList();
}
public void updateEmployee(int x, EmployeeTable employeeTable)
{
if (employeeTable.E_ID == x)
{
sampleDBEntities.Entry(employeeTable).State = EntityState.Modified;
sampleDBEntities.SaveChanges();
}
}
public void deleteEmployee(int id)
{
var employee = sampleDBEntities.EmployeeTables.Find(id);
if(employee !=null)
{
sampleDBEntities.Entry(employee).State = EntityState.Deleted;
sampleDBEntities.SaveChanges();
}
}
Angular Service:
angular.module('mainApp', []).
factory('employeeService', function ($http) {
var baseAddress = 'http://localhost:53254/api/employee/';
//var baseAddress = 'http://localhost:49595/MobileService/api/UserService/';
var url = "";
return {
getEmployeesList: function () {
url = baseAddress;
return $http.get(url);
},
getUser: function (employee) {
url = baseAddress + "Get/" + employee.E_id;
return $http.get(url);
},
addUser: function (employee) {
url = baseAddress + "post";
return $http.post(url, employee);
},
deleteUser: function (employee) {
url = baseAddress + "Delete/" + employee.E_Id;
return $http.delete(url);
},
updateUser: function (employee) {
url = baseAddress + "put/" + employee.E_Id;
return $http.put(url, employee);
}
};
});
Angular Controller:
angular.module('mainApp')
.controller('getEmployeeCrl', ['$scope', 'employeeService', function ($scope, employeeService) {
employeeService.getEmployeesList().then(function (response) {
$scope.employees = response.data;
}, function (err) {
$scope.errMessage = "Something wrong with server. Please try again.";
})
}]);
HTML:
<!DOCTYPE html>
<html ng-app="mainApp">
<head>
<title></title>
<meta charset="utf-8" />
<script src="../Scripts/angular.js"></script>
<script src="../mainApp.js"></script>
<script src="../Utilities/ConstantsFactory.js"></script>
<script src="../Services/EmployeeService.js"></script>
<script src="../Controllers/EmployeeController.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body ng-controller="getEmployeeCrl">
<div>
<h2 style="text-align:center; color:darkblue">PROJECT 51</h2>
</div>
<div class="container">
<div style="background-color:dimgray;color:white;padding:20px;">
<input type="button" value="Go to Employees" class="btn btn-info" />
<input type="button" value="Go to Clients" class="btn btn-info" style="width:145px" />
<button class="glyphicon glyphicon-plus btn btn-primary" value="Add" data-toggle="tooltip" data-placement="top" title="Add Data" style="float:right; width:50px"></button>
</div>
<br />
<table class="table table-bordered table table-condensed" ng-hide="!employees">
<thead style="background-color:palevioletred">
<tr style="text-decoration:solid;color:darkblue;">
<th>Id</th>
<th>Name</th>
<th>Job</th>
<th>Hire Date</th>
<th>Manager</th>
<th>Salary</th>
<th>Commission</th>
<th colspan="2">Edit/Delete</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="emp in employees ">
<td>{{emp.E_ID}}</td>
<td>{{emp.E_NAME}}</td>
<td>{{emp.E_JOB}}</td>
<td>{{emp.HIRE_DATE}}</td>
<td>{{emp.MANAGER_ID}}</td>
<td>{{emp.SALARY}}</td>
<td>{{emp.COMMISSION}}</td>
<td colspan="2" style="width:170px">
<input type="button" ng-model="emp.E_Id" value="View" class="btn btn-primary" style="width:70px" />
<input type="button" value="Edit" class="btn btn-primary" style="width:70px" />
<input type="button" value="Delete" class="btn btn-primary" style="width:70px" />
</td>
</tr>
</tbody>
</table>
<p class="alert alert-danger" ng-show="!employees">{{errMessage}} <span class="glyphicon glyphicon-refresh" ng-show="!employees"></span></p>
</div>
</body>
</html>
How can I make use of Bootstrap Modal for CRUD operations
Related
I am working on a sample app using angularJS.
I have a service which returns JSON object, I am trying to bind the response to a table.
Controller -
(function () {
var app = angular.module('searchApp', []);
app.config(function ($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist(['http://localhost:11838/SearchProfiles/get']);
});
var controller = app.controller('searchController', ["$scope", "$http", function ($scope, $http) {
$scope.profileName = "";
$http.get("http://localhost:11838/SearchProfiles/GetAllRuleSets")
.then(function (response) {
$scope.ruleSets = response.data;
});
$scope.searchProfiles = function () {
$http.get("http://localhost:11838/SearchProfiles/GetProfiles?profileName=" + $scope.profileName
+ "&ruleSetName=" + $scope.selectedRuleSet)
.then(function (response) {
$scope.showProfiles = true;
$scope.profiles = response.data;
console.log($scope.profiles);
});
};
$scope.clearForm = function () {
$scope.profileName = "";
$scope.selectedRuleSet = "";
};
}]);
})();
cshtml -
#{
ViewBag.Title = "Search Profiles";
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Search Profiles</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>
#Scripts.Render("~/Scripts/angular")
#Styles.Render("~/Content/css/scss")
</head>
<body ng-app="searchApp">
<div ng-controller="searchController" id="content">
<label>Profile Name: </label>
<input type="text" name="profileName" ng-model="profileName" /><br />
<label>RuleSet Name: </label>
<select ng-model="selectedRuleSet" ng-options="x for x in ruleSets"></select><br />
<button name="search" ng-click="searchProfiles()">Search</button>
<button name="clear" ng-click="clearForm()">Clear</button>
</div>
<div ng-controller="searchController" id="profilesDiv">
<table>
<tr ng-repeat="x in profiles">
<td>{{ x.ProfileName }}</td>
<td>{{ x.CreationDate }}</td>
</tr>
</table>
</div>
</body>
I am getting back following response -
[{"ProfileName":"Profile3","CreationDate":"1/9/2017"},{"ProfileName":"Profile4","CreationDate":"12/30/2016"}]
but even after setting $scope.profiles I am not seeing table on UI.
I'm fairly new to angular, am I missing something obvious.
Any help is appreciated.
The problem is that you are fetching the data in one scope (the div content where you declare ng-controller="searchController") and then trying to
view the data in another scope (the div profilesDiv where you again declare the ng-controller="searchController").
To solve the problem you need to remove ng-controller="searchController" from the profilesDiv and move the profilesDiv inside the content div.
Like this:
<body ng-app="searchApp">
<div ng-controller="searchController" id="content">
<label>Profile Name: </label>
<input type="text" name="profileName" ng-model="profileName" /><br />
<label>RuleSet Name: </label>
<select ng-model="selectedRuleSet" ng-options="x for x in ruleSets"></select><br />
<button name="search" ng-click="searchProfiles()">Search</button>
<button name="clear" ng-click="clearForm()">Clear</button>
<div id="profilesDiv">
<table>
<tr ng-repeat="x in profiles">
<td>{{ x.ProfileName }}</td>
<td>{{ x.CreationDate }}</td>
</tr>
</table>
</div>
</div>
</body
I'm creating a contacts list and i'm using Angular for the first time.
I created an attribute directive for the table rows (that i use inside the table tag), in which i add a controller to handle click on a button, that deletes the row removing it from the table.
All works well, but i get an error in the browser console.
Here you can see the output of my source code:
When i try to delete a contact (i press on the button with the trash as icon) it works, but in the Chrome console i get this error:
Uncaught ReferenceError: delUser is not defined
You can see it here:
Here is my code:
index.html
<!doctype html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="utf-8">
<title>Rubrica</title>
<link rel="stylesheet" href="css/app.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body ng-controller="myCtrl">
<section id="panel">
<button class="panel_btn" ng-click="showHideAdd()"><i class="fa fa-plus"></i> Aggiungi</button>
<button class="panel_btn" ng-click="showHideSearch()"><i class="fa fa-search"></i> Cerca</button>
</section>
<section id="list">
<table width="50%">
<thead>
<tr>
<th>Nome<span ng-show="sortType == 'name' && !sortReverse" class="fa fa-caret-down"></span><span ng-show="sortType == 'name' && sortReverse" class="fa fa-caret-up"></span></th>
<th>Cognome<span ng-show="sortType == 'surname' && !sortReverse" class="fa fa-caret-down"></span><span ng-show="sortType == 'surname' && sortReverse" class="fa fa-caret-up"></span></th>
<th>Telefono<span ng-show="sortType == 'phone' && !sortReverse" class="fa fa-caret-down"></span><span ng-show="sortType == 'phone' && sortReverse" class="fa fa-caret-up"></span></th>
<th>Operazioni</th>
</tr>
</thead>
<tbody>
<tr ng-hide="isSearchVisible">
<td><input name="nameSearch" placeholder="Cerca nome" ng-model="search.name"></input></td>
<td><input name="surnameSearch" placeholder="Cerca cognome" ng-model="search.surname"></input></td>
<td><input name="phoneSearch" placeholder="Cerca telefono" ng-model="search.phone"></input></td>
</tr>
<tr userdir item="user" onclick="delUser" ng-repeat="user in users | orderBy:sortType:sortReverse | filter:search">
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">Totale utenti: {{getTotal()}}</td>
</tr>
</tfoot>
</table>
</section>
<section id="tools">
<form name="addForm" ng-show="isAddVisible" novalidate>
<p>Compila tutti i campi</p>
<input type="text" name="nameToAdd" placeholder="Nome" ng-model="formName" required ng-minlength="3"><br /><small ng-show="isInvalid && (addForm.nameToAdd.$error.required || addForm.nameToAdd.$error.minlength)">Il nome deve avere almeno 3 lettere</small><br />
<input type="text" name="surnameToAdd" placeholder="Cognome" ng-model="formSurname" required ng-minlength="3"><br /><small ng-show="isInvalid && (addForm.surnameToAdd.$error.required || addForm.surnameToAdd.$error.minlength)">Il cognome deve avere almeno 3 lettere</small><br />
<input type="tel" name="phoneToAdd" placeholder="Telefono" ng-model="formPhone" required ng-pattern="/^\d{2,4}/\d{5,8}/"><br /><small ng-show="isInvalid && (addForm.phoneToAdd.$error.required || addForm.phoneToAdd.$error.pattern)">Inserisci un numero di telefono valido</small><br />
<button ng-click="add()"><i class="fa fa-save fa-lg"></i> Salva</button>
</form>
<form name="searchForm" ng-submit="search()" ng-show="isSearchVisible" novalidate>
<p>Cerca utenti</p>
<input type="text" name="stringToFind" placeholder="Cerca..." ng-model="search.$" required><br />
</form>
</section>
</body>
</html>
app.js
var myApp = angular.module('myApp', []);
myApp.controller('myCtrl', ['$scope', '$timeout', function($scope, $timeout) {
$scope.sortType = 'name';
$scope.sortReverse = false;
$scope.isInvalid = false;
$scope.users = [{
name: 'Mario',
surname: 'Rossi',
phone: '084/8465645'
}, {
name: 'Giuseppe',
surname: 'Bianchi',
phone: '06/548484'
}, {
name: 'Luca',
surname: 'Verde',
phone: '0984/3214867'
}, {
name: 'Luigi',
surname: 'Roma',
phone: '0775/3214867'
}];
$scope.getTotal = function() {
return $scope.users.length;
};
$scope.add = function() {
if ($scope.addForm.$valid) {
$scope.users.push({
name: $scope.formName,
surname: $scope.formSurname,
phone: $scope.formPhone
});
$scope.formName = '';
$scope.formSurname = '';
$scope.formPhone = '';
} else {
$scope.isInvalid = true;
}
};
$scope.isAddVisible = false;
$scope.showHideAdd = function() {
$scope.isAddVisible = $scope.isAddVisible ? false : true;
$scope.isSearchVisible = false;
};
$scope.isSearchVisible = false;
$scope.showHideSearch = function() {
$scope.isSearchVisible = $scope.isSearchVisible ? false : true;
$scope.isAddVisible = false;
};
$scope.delUser = function(user) {
var index = $scope.users.indexOf(user);
$scope.users.splice(index, 1);
};
}]);
myApp.directive('userdir', function() {
return {
restrict: 'A',
templateUrl: 'views/userRow.html',
controller: function($scope) {
$scope.delete = function() {
$scope.onclick($scope.item);
};
},
controllerAs: 'ctrl',
scope: {
item: '=',
onclick: '='
}
};
});
userRow.html
<tr>
<td>{{item.name}}</td>
<td>{{item.surname}}</td>
<td>{{item.phone}}</td>
<td><button ng-click="delete()"><i class="fa fa-trash"></i></button>
</tr>
Renaming onclick to any other word inside my directive's scope makes it working.
The glitch may happen because onclick is a reserved word for HTML.
It looks like your "ng-click=delete()" already calls the delUser function.
Why do you need the onclick="delUser" in index.html?
Also if this a function should it be onclick="delUser()"?
I have an angular app, and an index.html with an ng-view that renders to different views partials/persons.html and partials/newPerson.html.
when i add a new person to the $scope.persons in my controller via the newPerson.html the $scope.persons is updated, but it dosn´t updated the table in the partials/persons.html. if i copy/paste the table into partials/newPerson.html the table is updated automatically. I cant seem to wrap my head around why? they are using the same controller...?
thank´s in advance for your help :)
js/app.js
var app = angular.module('app',['ngRoute']);
app.config(function($routeProvider){
$routeProvider
.when('/persons',{
templateUrl:'partials/persons.html',
controller:'PersonCtrl'
})
.when('/newperson',{
templateUrl:'partials/newPerson.html',
controller:'PersonCtrl'
})
.otherwise({
redirectTo: '/'
});
});
app.controller('PersonCtrl',['$scope', function($scope){
var persons = [
{
id: 1
,name: "Jens",
age : 18}
,{
id: 2,
name: "Peter",
age : 23
}
,{
id: 3
,name: "Hanne"
,age : 23
}
];
$scope.persons = persons;
$scope.nextId = 4;
$scope.savePerson = function(){
if($scope.newPerson.id === undefined)
{
$scope.newPerson.id= $scope.nextId++;
$scope.persons.push($scope.newPerson);
}else{
for (var i = 0; i < $scope.persons.length; i++) {
if($scope.persons[i].id === $scope.newPerson.id){
$scope.persons[i] = $scope.newPerson;
break;
}
}
}
$scope.newPerson = {};
};
index.html
<html ng-app="app" ng-controller="PersonCtrl">
<head>
<title>Routing</title>
<link rel="stylesheet" href="css/styles.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0rc1/angular-route.js"></script>
<script src="https://code.angularjs.org/1.4.7/i18n/angular-locale_da.js"></script>
<script src="angularSrc/app.js"></script>
</head>
<body>
<br>
<div class="container">
<header>
<h1>People Routing</h1>
<nav>
persons
new person
</nav>
</header>
<div ng-view="ng-view">
</div>
</div>
</body>
partials/persons.html
<h3>Persons</h3>
<table >
<thead>
<tr>
<td>Id</td>
<td>name</td>
<td>age</td>
</tr>
</thead>
<tbody>
<tr ng-repeat="p in persons">
<td>{{p.id}} </td>
<td>{{p.name}} </td>
<td>{{p.age}} </td>
</tr>
</tbody>
</table>
partials/newPerson.html
<div >
<h1>New person</h1>
<form class="form-horizontal">
<fieldset>
<div class="form-group">
<input type="text" ng-model="newPerson.name" model="newPerson.name" class="form-control" id="year" placeholder="name">
</div>
<div class="form-group">
<input type="number" ng-model="newPerson.age" model="newPerson.age" class="form-control" id="age" placeholder="age">
</div>
</fieldset>
</form>
<button type="submit" ng-click="savePerson()" >Save</button>
<h2>nextId: {{nextId}}</h2>
</div>
The problem is that you aren't realizing that each use of controller creates new instance.
The scope is destroyed when you leave a controller so if you add to the scope in one instance , that change will be lost when you load controller again on your other route.
You need to use a service to persist the data during the life of each page load.
Simple service example:
app.factory('PersonService', function () {
var persons = [{
id: 1,
name: "Jens",
age: 18
}, {
...
}, {
...
}];
return {
persons: persons
}
});
Then inject in controller and use the service data in controller
app.controller('PersonCtrl',['$scope','PersonService', function($scope,PersonService){
$scope.persons = PersonService.persons;
I setting up a scenario very similar to the Editable Row example from the x-editable demo site. In this scenario, a there is a simple table with three columns for data and a fourth for edit and delete buttons. A third button outside of the table adds a row to the table. When the form is editable, the data columns become editable (the primary feature of x-editable library). For this demo, the first column becomes a simple text edit and the second two columns become drop lists.
The table is created by having an ng-repeat on a row template. I need to do a few different things that all involve accessing the scope created by the ng-repeat. I need to
detect when the row is editable and when it is not
filter the options for the second drop list when the first drop list changes
In order to try to work with this demo, I've added a controller for the individual row. That has given me some access to the form (name = rowform), but I'm still not able to set a watch on the "make" property. I can't even find what property of the form is changing when the user makes a selection.
How do I set up a watch on the 'make' property?
Page Controller
angular.module('app').controller("quoteBuckingRaterController",
function ($scope, $q, $filter, listService, transactionDataService) {
$scope.equipment = [];
$scope.makes = [];
$scope.models = [];
$scope.showModel = function(equip) {
if(equip.model) {
var selected = $filter('filter')($scope.models, {id: equip.model});
return selected.length ? selected[0].name : 'Not set';
} else {
return 'Not set';
}
};
$scope.showMake = function(equip) {
if (equip.model) {
var selected = $filter('filter')($scope.models, { id: equip.model });
if (selected.length && selected.length > 0) {
if (equip.make != selected[0].make)
equip.make = selected[0].make;
return selected[0].make;
}
else {
return 'Not set';
}
} else {
return 'Not set';
}
};
$scope.checkName = function (data, id) {
if (!data) {
return "Description is required";
}
};
$scope.checkModel = function (data, id) {
if (!data) {
return "Model is required";
}
};
$scope.saveEquipment = function (data, id) {
$scope.inserted = null;
};
$scope.cancelRowEdit = function (data, id) {
$scope.inserted = null;
};
$scope.removeEquipment = function(index) {
$scope.equipment.splice(index, 1);
};
$scope.addEquipment = function() {
$scope.inserted = {
id: $scope.equipment.length+1,
name: '',
make: null,
model: null
};
$scope.equipment.push($scope.inserted);
};
$scope.filterModels = function (make) {
$scope.models = _.where($scope.allModels, function(item) {
return item.make == make;
});
};
//called by another process when page loads
$scope.initialize = function (loaded) {
return $q(function (resolve, reject) {
if (!loaded) {
listService.getEquipmentModels().then(function (data) {
$scope.allModels = data;
$scope.models = data;
//uses underscore.js
$scope.makes = _.chain(data)
.map(function (item) {
var m = {
id: item.make,
name: item.make
};
return m;
})
.uniq()
.value();
resolve();
});
}
});
}
});
Row Controller
angular.module('app').controller("editRowController",
function ($scope) {
$scope.testClick = function () {
alert('button clicked');
};
$scope.make = null;
$scope.$watch('make', function () {
alert('how do I tell when the make has been changed?');
this.$parent.$parent.filterModels(make.id);
});
});
HTML
<div>
<div class="col-md-12" style="margin-bottom: 3px">
<div class="col-md-4 col-md-offset-1" style="padding-top: 6px; padding-left: 0px"><label>Equipment</label></div>
<div class="col-md-offset-10">
<button class="btn btn-primary btn-sm" ng-click="addEquipment()">Add row</button>
</div>
</div>
<div class="col-md-10 col-md-offset-1">
<table class="table table-bordered table-hover table-condensed">
<tr style="font-weight: bold; background-color: lightblue">
<td style="width:35%">Name</td>
<td style="width:20%">Make</td>
<td style="width:20%">Model</td>
<td style="width:25%">Edit</td>
</tr>
<tr ng-repeat="equip in equipment" ng-controller="editRowController">
<td>
<!-- editable equip name (text with validation) -->
<span editable-text="equip.name" e-name="name" e-form="rowform" onbeforesave="checkName($data, equip.id)" e-required>
{{ equip.name || 'empty' }}
</span>
</td>
<td>
<!-- editable make (select-local) -->
<span editable-select="equip.make" e-name="make" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
{{ showMake(equip) }}
</span>
</td>
<td>
<!-- editable model (select-remote) -->
<span editable-select="equip.model" e-name="model" e-form="rowform" e-ng-options="g.id as g.name for g in models" onbeforesave="checkModel($data, equip.id)" e-required>
{{ showModel(equip) }}
</span>
<button type="button" ng-disabled="rowform.$waiting" ng-click="testClick()" class="btn btn-default">
test
</button>
</td>
<td style="white-space: nowrap">
<!-- form -->
<form editable-form name="rowform" onbeforesave="saveEquipment($data, equip.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == equip">
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">
save
</button>
<button type="button" ng-disabled="rowform.$waiting" ng-click="rowform.$cancel()" class="btn btn-default">
cancel
</button>
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-primary" ng-click="rowform.$show()">edit</button>
<button class="btn btn-danger" ng-click="removeEquipment($index)">del</button>
</div>
</td>
</tr>
</table>
</div>
</div>
ng-repeat creates a child scope for each row (for each equipment). The scope of the EditRowController is therefore a childScope of the parent quoteBuckingRaterController.
This childScope contains:
all properties of the parent scope (e.g. equipment, makes, models)
the property equip with one value of the equipment array, provided by ng-repeat
any additional scope property that is defined inside the ng-repeat block, e.g. rowform
Therefore you are able to access these properties in the childController editRowController using the $scope variable, e.g.:
$scope.equip.make
$scope.equipment
and inside the ng-repeat element in the html file by using an angular expression, e.g:
{{equip.make}}
{{equipment}}
Now to $scope.$watch: If you provide a string as the first argument, this is an angular expression like in the html file, just without surrounding brackets {{}}. Example for equip.make:
$scope.$watch('equip.make', function (value) {
console.log('equip.make value (on save): ' + value);
});
However, angular-xeditable updates the value of equip.make only when the user saves the row. If you want to watch the user input live, you have to use the $data property in the rowform object, provided by angular-xeditable:
$scope.$watch('rowform.$data.make', function (value) {
console.log('equip.make value (live): ' + value);
}, true);
You can also use ng-change:
<span editable-select="equip.make" e-name="make" e-ng-change="onMakeValueChange($data)" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
JS:
$scope.onMakeValueChange = function(newValue) {
console.log('equip.make value onChange: ' + newValue);
}
That should solve your first question: How to watch the make property.
Your second question, how to detect when the row is editable and when it is not, can be solved by using the onshow / onhide attributes on the form or by watching the $visible property of the rowform object in the scope as documented in the angular-xeditable reference
<form editable-form name="rowform" onshow="setEditable(true)" onhide="setEditable(false)">
$scope.setEditable = function(value) {
console.log('is editable? ' + value);
};
// or
$scope.$watch('rowform.$visible', function(value) {
console.log('is editable? ' + value);
});
You might ask why the rowform object is in the current childScope.
It is created by the <form> tag. See the Angular Reference about the built-in form directive:
Directive that instantiates FormController.
If the name attribute is specified, the form controller is published
onto the current scope under this name.
A working snippet with your example code:
angular.module('app', ["xeditable"]);
angular.module('app').controller("editRowController", function ($scope) {
$scope.testClick = function () {
alert('button clicked');
};
$scope.$watch('equip.make', function (value) {
console.log('equip.make value (after save): ' + value);
});
$scope.$watch('rowform.$data.make', function (value) {
console.log('equip.make value (live): ' + value);
}, true);
// detect if row is editable by using onshow / onhide on form element
$scope.setEditable = function(value) {
console.log('is equip id ' + $scope.equip.id + ' editable? [using onshow / onhide] ' + value);
};
// detect if row is editable by using a watcher on the form property $visible
$scope.$watch('rowform.$visible', function(value) {
console.log('is equip id ' + $scope.equip.id + ' editable [by watching form property]? ' + value);
});
});
angular.module('app').controller("quoteBuckingRaterController", function ($scope, $filter) {
$scope.equipment = [];
$scope.makes = [{value: 1, name: 'Horst'}, {value: 2, name: 'Fritz'}];
$scope.models = [{id: 1, name: 'PC', make: 1}];
$scope.showModel = function(equip) {
if(equip.model) {
var selected = $filter('filter')($scope.models, {id: equip.model});
return selected.length ? selected[0].name : 'Not set';
} else {
return 'Not set';
}
};
$scope.showMake = function(equip) {
if (equip.model) {
var selected = $filter('filter')($scope.models, { id: equip.model });
if (selected.length && selected.length > 0) {
if (equip.make != selected[0].make)
equip.make = selected[0].make;
return selected[0].make;
}
else {
return 'Not set';
}
} else {
return 'Not set';
}
};
$scope.checkName = function (data, id) {
if (!data) {
return "Description is required";
}
};
$scope.checkModel = function (data, id) {
if (!data) {
return "Model is required";
}
};
$scope.saveEquipment = function (data, id) {
$scope.inserted = null;
};
$scope.cancelRowEdit = function (data, id) {
$scope.inserted = null;
};
$scope.removeEquipment = function(index) {
$scope.equipment.splice(index, 1);
};
$scope.addEquipment = function() {
$scope.inserted = {
id: $scope.equipment.length+1,
name: '',
make: null,
model: null
};
$scope.equipment.push($scope.inserted);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-xeditable/0.1.9/js/xeditable.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/angular-xeditable/0.1.9/css/xeditable.css" rel="stylesheet"/>
<link href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"/>
<div ng-app="app" ng-controller="quoteBuckingRaterController">
<div class="col-md-12" style="margin-bottom: 3px">
<div class="col-md-4 col-md-offset-1" style="padding-top: 6px; padding-left: 0px"><label>Equipment</label></div>
<div class="col-md-offset-10">
<button class="btn btn-primary btn-sm" ng-click="addEquipment()">Add row</button>
</div>
</div>
<div class="col-md-10 col-md-offset-1">
<table class="table table-bordered table-hover table-condensed">
<tr style="font-weight: bold; background-color: lightblue">
<td style="width:35%">Name</td>
<td style="width:20%">Make</td>
<td style="width:20%">Model</td>
<td style="width:25%">Edit</td>
</tr>
<tr ng-repeat="equip in equipment" ng-controller="editRowController">
<td>
<!-- editable equip name (text with validation) -->
<span editable-text="equip.name" e-name="name" e-form="rowform" onbeforesave="checkName($data, equip.id)" e-required>
{{ equip.name || 'empty' }}
</span>
</td>
<td>
<!-- editable make (select-local) -->
<span editable-select="equip.make" e-name="make" e-form="rowform" e-ng-options="s.value as s.name for s in makes">
{{ showMake(equip) }}
</span>
</td>
<td>
<!-- editable model (select-remote) -->
<span editable-select="equip.model" e-name="model" e-form="rowform" e-ng-options="g.id as g.name for g in models" onbeforesave="checkModel($data, equip.id)" e-required>
{{ showModel(equip) }}
</span>
<button type="button" ng-disabled="rowform.$waiting" ng-click="testClick()" class="btn btn-default">
test
</button>
</td>
<td style="white-space: nowrap">
<!-- form -->
<form editable-form name="rowform" onbeforesave="saveEquipment($data, equip.id)" ng-show="rowform.$visible" class="form-buttons form-inline" shown="inserted == equip" onshow="setEditable(true)" onhide="setEditable(false)">
<button type="submit" ng-disabled="rowform.$waiting" class="btn btn-primary">
save
</button>
<button type="button" ng-disabled="rowform.$waiting" ng-click="rowform.$cancel()" class="btn btn-default">
cancel
</button>
</form>
<div class="buttons" ng-show="!rowform.$visible">
<button class="btn btn-primary" ng-click="rowform.$show()">edit</button>
<button class="btn btn-danger" ng-click="removeEquipment($index)">del</button>
</div>
</td>
</tr>
</table>
</div>
</div>
If you simply want to $watch the make property of equipment, try changing to:
$scope.$watch('equipment.make', function(){(...)})
You could write your own directive for this.
The main advantage is that directives have isolated scope and can have their own controller.
see the directive documentation to know if it's for you.
I need to add a custom directive to my code, but every time I add it, it breaks my code. I checked the console and is giving me the following error
Error: Argument 'guessGameController' is not a function, got undefined
at Error (native)
Now I am not sure if I am not setting my code right or if I am not adding the directive where is supposed to go. Here is my code, I appreciate all the help.
index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="guessGameApp">
<head>
<title>Word Game 2.0 - AngularJS</title>
<!--Encoding-->
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<!-- JQuery -->
<script src="js/jquery-1.11.0.min.js"></script>
<!--Scripts-->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js" type="text/javascript"></script>
<script src="js/controllers/app.js" type="text/javascript"></script>
<script src="js/controllers/maincontroller.js" type="text/javascript"></script>
<!--Styles-->
<link rel="stylesheet" type="text/css" href="css/magicWord.css">
<!--<script src="js/jquery-1.11.0.min.js"></script>-->
</head>
<body>
<div ng-controller="guessGameController">
<p>
<header id="masthead">
<h2 align="center">{{appTitle}}</h2>
</header>
</p>
<div ng-controller="wordController">
<p>
<table align="center" width="300px" height="150px" border="solid 2px">
<tr>
<td id="guessBox">
<p align="center">
<input value="" type="text" id="guestGuess" placeholder="Enter Guess" ng-model="guestGuess"/>
</p>
<p align="center"><button ng-click="addGuess()" id="guessButton">Click to Check</button></p>
</td>
</tr>
<tr>
<td>
<h3 align="center">Your guesses so far are: </h3>
<p align="center" ng-repeat="words in guesses">{{words}}</p>
</td>
</tr>
<tr>
<td>
<p align="center">You have guessed:<b>{{guessed}}</b> times out {{allowed}} chances.</p>
<p align="center">You have <b>{{allowed - guessed}}</b> guesses left.</p>
</td>
</tr>
<tr>
<td>
<a custom-button>Click me</a>
<br />
<button custom-button>Hello</button>
</td>
</tr>
</table>
</p>
</div>
</div>
</body>
</html>
app.js
var gameApp = angular.module('guessGameApp', []);
var gameTemplate = angular.module('guessGameApp', []);
maincontroller.js
gameApp.controller("guessGameController", function($scope)
{
$scope.appTitle = "WELCOME TO THE GUESS GAME!";
});
gameApp.controller('wordController', function($scope)
{
$scope.guess = '';
$scope.guesses = [];
$scope.guessed= '';
$scope.allowed = 6;
$scope.wordToGuess = "Just";
$scope.pushGuess = function () {
$scope.guesses.push($scope.guestGuess);
$scope.guessed = $scope.guesses.length;
$scope.resetGuess();
}
$scope.resetGuess = function() {
$scope.guestGuess = '';
}
$scope.addGuess = function()
{
if ($scope.guestGuess == null || $scope.guestGuess == '')
{
$("input[type=text]").ready(function () { $("#guestGuess").addClass("blur"); });
$scope.result = " Please enter a guess\n\nDon't leave the box empty.";
alert($scope.result);
}
else if ($scope.guestGuess.toLowerCase() == $scope.wordToGuess.toLowerCase())
{
$("input[type=text]").ready(function () { $("#guestGuess").removeClass("blur"); });
$scope.pushGuess(guestGuess);
$scope.result = "You have guessed the correct word. Way to go!\n\n\t\t The word was: ";
alert($scope.result + $scope.wordToGuess);
}
else if ($scope.guestGuess != $scope.wordToGuess & ($scope.allowed - $scope.guessed) > 1)
{
$("input[type=text]").ready(function () { $("#guestGuess").removeClass("blur"); });
$scope.pushGuess(guestGuess);
$scope.result = "Please try again!";
alert($scope.result);
}
else if (($scope.allowed - $scope.guessed) <= 1)
{
$("input[type=text]").ready(function () { $("#guestGuess").addClass("doneBlur"); });
$scope.guesses.push($scope.guestGuess);
$scope.guessed = $scope.guesses.length;
$scope.result = "Game over! The word was: ";
alert($scope.result + $scope.wordToGuess);
}
$scope.guess = '';
}
});
gameApp.directive('customButton', function ()
{
$scope.wordToGuess = "Just";
return {
restrict: 'A',
replace: true,
transclude: true,
templateUrl: '../../templates/customTemplate.HTML',
link: function (scope, element, attrs)
{
element.bind("click",function()
{
alert("The value of 'guessWord' is " + scope.wordToGuess);
})
}};
});
customTemplate.html
<a href="" class="myawesomebutton" ng-transclude>
<i class="icon-ok-sign"></i>
</a>
In app.js remove the second module declaration
var gameApp = angular.module('guessGameApp', []);
//var gameTemplate = angular.module('guessGameApp', []); --> remove this line
You are also modifying DOM from the controller, this is not the angular way. If you want to add classes when some condition occurs, then have a look at ng-class