When programming in ASP.NET MVC I became accustomed to using partial views for CRUD operations. And now that I'm working with Spring + AngularJS I was hoping I could work in a similar way. So far, with the use of angular-ui-router I've managed to accomplish some basic partialing for create/update.
Example:
layout.html
<!doctype html>
<html lang="en" ng-app="Example">
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<div class="container">
<nav class="row">
<ul class="nav nav-pills">
<li ui-sref-active="active"><a ui-sref="clients">Clients</a></li>
</ul>
</nav>
</div>
<div class="container">
<div class="row">
<div class="col-md-12" ui-view></div>
</div>
</div>
<script src="libs/jquery/dist/jquery.min.js"></script>
<script src="libs/angular/angular.min.js"></script>
<script src="libs/angular-ui-router/release/angular-ui-router.min.js"></script>
<script src="appjs/app.js"></script>
<script src="appjs/services.js"></script>
<script src="appjs/controllers/ClientController.js"></script>
<script src="appjs/filters.js"></script>
<script src="appjs/directives.js"></script>
</body>
</html>
/client/create.html
<h1>Create new client</h1>
<form name="clientForm">
<div ui-view></div>
</form>
/client/edit.html
<h1>Edit client</h1>
<form name="clientForm" ng-show="!loading">
<input type="hidden" name="id" ng-model="client.id" />
<div ui-view></div>
</form>
/client/_createOrEdit.html
<div class="form-group" ng-class="{'has-error': clientForm.name.$invalid}">
<label for="name">Name:</label>
<input id="name"
name="name"
type="text"
ng-model="client.name"
class="form-control"
required />
<span class="help-block" ng-show="clientForm.name.$error.required">Name field is required</span>
</div>
<div class="form-group" ng-class="{'has-error': clientForm.address.$invalid}">
<label for="address">Address:</label>
<input id="address"
name="address"
type="text"
ng-model="client.address"
class="form-control"
required />
<span class="help-block" ng-show="clientForm.address.$error.required">Address field is required</span>
</div>
<button class="btn btn-primary btn-lg btn-block"
ng-click="addOrUpdate(client)"
ng-disabled="clientForm.$invalid || clientForm.$pristine">Save</button>
app.js
App.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
/* Client */
.state("clients", {
url : "/clients",
templateUrl : "clients/index",
controller : ClientListController
})
.state("clientCreate", {
abstract : true,
url : "/client",
templateUrl : "clients/create",
controller : ClientCreateController
})
.state("clientCreate.createOrEdit", {
url : "/create",
templateUrl : "clients/createOrEdit",
})
.state("clientEdit", {
abstract: true,
url : "/client/{id:[0-9]{1,9}}",
templateUrl : "clients/edit",
controller : ClientEditController
})
.state("clientEdit.createOrEdit", {
url : "/edit",
templateUrl : "clients/createOrEdit",
})
});
ClientController.js
var ClientListController = function($scope, $http) {
$scope.error = false;
$scope.loading = false;
$scope.getClientList = function() {
$scope.loading = true;
$http.get('clients/clientlist.json')
.success(function(clientList) {
$scope.clients = clientList;
$scope.loading = false;
})
.error(function() {
$scope.error = true;
$scope.loading = false;
});
}
$scope.getClientList();
};
var ClientCreateController = function($scope, $http, $location) {
$scope.client = {};
$scope.loading = false;
$scope.addOrUpdate = function(client) {
client.id = null;
$scope.loading = true;
$http.post('clients/createOrEdit', client)
.success(function() {
$scope.loading = false;
$location.path('/clients').replace(); //redirect to index
});
};
};
var ClientEditController = function($scope, $stateParams, $http, $location) {
$scope.client = {};
$scope.error = false;
$scope.loading = false;
$scope.getClientById = function(id) {
$scope.loading = true;
$http.get('clients/' + id + '/client.json')
.success(function(client) {
$scope.client = client;
$scope.loading = false;
})
.error(function() {
$scope.error = true;
$scope.loading = false;
});
}
$scope.addOrUpdate = function(client) {
$scope.loading = true;
$http.post('clients/createOrEdit', client)
.success(function() {
$scope.loading = false;
$location.path('/clients').replace(); //redirect to index
});
};
var selectedId = $stateParams.id;
$scope.getClientById(selectedId);
}
This is my current setup and in itself I'm quite happy with it. However when trying to expand this by adding a bit more complexity, I've gotten stuck.
Let's say that our clients can have multiple locations, so now each client has a list of locations.
What I'd like to do is make partial views for creating/editing locations and when editing or creating clients show them for each existing location the client has, the ability to remove them, and the ability to add new locations, all on the client create/edit page.
In ASP.NET MVC I'd be able to do this with partial views, BeginCollectionItem, and a bit of jQuery. How should I approach this with angular?
I tried using ng-repeat with ng-include inside, however when trying to add/remove locations nothing was reflected onto ng-repeat, and also I didn't like the approach.
I assume that the reason why nothing happened upon clicking the button has something to do with me receiving Async XHR deprecated warning in console upon clicking the button with insertLocation method.
insertLocation()
looks like this
$scope.insertLocation = function() {
$scope.client.locations.push({});
}
and placed inside create and edit controllers, and also the client var is edited to look like this
$scope.client = {locations: []};
Please note. I'm not exactly looking for a fix to this situation(although it would help), but more of an advice on my approach. Is it wrong? If it is what would be the proper mindset, proper approach?