I using ui-router for the first time. I've check some tutorial, but nerver been able to make my project work.
I have a page with a controler, my file home.htm get request is complete but the ui-view do not display it.
index.htm
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.min.js"></script>
<script ng-include="'http://192.168.0.110/home.htm'"></script>
angular
.module('app', [
'ui.router'
])
.config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('/', {
url:"/",
templateUrl: 'http://192.168.0.110/home.htm',
controller:'temperatureCTRL'
})
}])
.controller('temperatureCTRL', ["$scope", "$interval", "ArduinoService", function($scope, $interval, service) {
var autoRefresh;
$scope.channels = [];
function startRefresh(){
autoRefresh = $interval(function() {
updateAjax();
}, 5000);
}
function updateAjax() {
service.getChannels(function(err, result) {
if (err) {
return alert(err);
}
// puis les mets dans le scope
$scope.channels = result.channels;
})
};
$scope.init = function() { //on load page first get data
updateAjax();
startRefresh()
}
$scope.switchChannel = function($scope, channel) { // change name function
var switchCh = {canal : $scope.canal, status : $scope.status}
service.switchChannel(switchCh, function() {
});
updateAjax();
};
$scope.channelsClk = function($scope, channel) {
var chanObj = {setPoint : $scope.setPoint, name : $scope.name, canal : $scope.canal
};
service.putChannels(chanObj, function() {
});
}
$scope.stopRefresh = function() { //ng-mousedown
$interval.cancel(autoRefresh);
};
$scope.restartRefresh = function() {
startRefresh();
console.log('lost focus');
};
$scope.$on('$destroy', function() {
// Make sure that the interval is destroyed too
$scope.restartRefresh();
});
}])
.service('ArduinoService', ['$http', function($http) {
return {
getChannels: function(cb) {
$http.get('http://192.168.0.110/ajax_inputs')
.success(function(result) {
cb(null, result);
});
},
switchChannel: function(switchCh, cb) {
$http.put('http://192.168.0.110/switch', {
switchCh
})
.success(function(result) {
cb(null, true);
});
},
putChannels: function(channels, cb) {
$http.put('http://192.168.0.110/channels', {
channels
})
.success(function(result) {
cb(null, true);
});
}
};
}])
now home.htm
<!DOCTYPE html><!-- directive de repeat sur les données de vue channels -->
<div class="IO_box" ng-repeat="channel in channels">
<h2>{{channel.canal}}</h2>
<button type="button" ng-click="switchChannel(channel, channel.canal)" ng-model="channel.status"> {{channel.status}} </button>
<h2>
<form> name:<br>
<input type="text" name="Name" size="6" autocomplete="off" ng-model="channel.name" ng-focus="stopRefresh()" ng-blur="restartRefresh()">
<button ng-click="channelsClk(channel, channel.name)">change</button>
</form>
</h2>
<br>
<h4><span class="Ainput" >{{channel.temperature}}ºC</span></h4>
<h2>Setpoint
<input type="number" name="setpoint" ng-model="channel.setPoint" ng-focus="stopRefresh()" ng-blur="restartRefresh()"
min="5" max="30" step="0.5" size="1"> ºC <br />
<button ng-click="channelsClk(channel, channel.setPoint)">ok</button>
</h2>
<h5>state:
<span class="permRun">{{channel.permRun}}</span>
</h5>
<h5>
<span class="AoutputChannel">{{channel.percent_out}}%</span>
</h5>
There's no need to put the domain in the templateUrl. You're also trying to fetch it from your internal ip address but I think really you want to hit localhost which can just be written as http://localhost:PORT where PORT is whatever port your server is running on.
So your state definition should be:
$stateProvider
.state('/', {
url:"/",
templateUrl: './home.htm', //assumes this template is in the same dir as the state definition file
controller:'temperatureCTRL'
});
Also, if you want to display other templates you need to add <div ui-view></div> to your home.htm file so that ui-router knows where to display any nested templates (as Simon H commented there is no need for the ng-include).
Not related to ui-router but you shouldn't need to hardcode the domain name into your $http requests. Angular will use the current domain if you just write e.g. '/ajax_inputs' (which should be fine in your case because you are attempting to hit localhost). If you want to hit another domain then put that in a variable that can be easily changed.
getChannels: function(cb) {
$http.get('/ajax_inputs')
.then(function(result) { // .then with callback for error handling (success and error methods are being deprecated)
cb(null, result);
}, function(error) {
// handle error here
});
}
Related
I went through hundreds of pages for several days without success and here is my problem.
I use the MEAN stack and at this point I have a simple form that works very well to save a "name" field to a MongoDB collection. Now, I would like, client-side, add an image upload and, on form submit, store the image on my server and finally save the "name" field and the image path to the MongoDB collection.
AngularJS side, I tried using ng-file-upload with multer server-side. I have done well to operate for the upload of the file but only that. But after hundreds of tests, I despair. Here is an extract of my original code without file upload.
Server side
sections.server.model
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var SectionSchema = new Schema({
name: {
type: String,
default: '',
trim: true,
required: true,
unique: true
},
image: {
type: String,
default: ''
}
});
mongoose.model('Section', SectionSchema);
sections.server.controller
exports.create = function (req, res) {
var section = new Section(req.body);
section.save(function (err) {
if (err) {
return res.status(400).send({
message: getErrorMessage(err)
});
} else {
res.json(section);
}
});
};
sections.server.routes
var sections = require('../../app/controllers/sections.server.controller');
module.exports = function (app) {
app.route('/api/sections')
.post(sections.create);
};
Client side
sections.client.module
'use strict';
var sections = angular.module('sections', []);
sections.client.controller
'use strict';
angular.module('sections')
.controller('SectionsController',
['$scope', '$routeParams', '$location', 'Sections'
function ($scope, $routeParams, $location, Sections) {
$scope.create = function () {
var section = new Sections({
name: this.name
});
section.$save(function (response) {
$location.path('sections/' + response._id);
}, function (errorResponse) {
$scope.error = errorResponse.data.message;
});
};
}]);
sections.client.routes
angular.module('sections').config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/sections', {
controller: 'SectionsController',
templateUrl: 'sections/views/list-sections.client.view.html'
})
.when('/sections/create', {
controller: 'SectionsController',
templateUrl: 'sections/views/create-section.client.view.html'
})
.otherwise({
redirectTo: '/'
});
}]);
sections.client.service
'use strict';
angular.module('sections').factory('Sections', ['$resource', function ($resource) {
return $resource('api/sections/:sectionId', {
sectionId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}]);
create-section.client.view
<section>
<h1>New Article</h1>
<form data-ng-submit="create()" novalidate>
<div>
<label for="name">Nom du rayon</label>
<div>
<input type="text" data-ng-model="name" id="name" placeholder="Name" required>
</div>
</div>
<div>
<input type="submit">
</div>
<div data-ng-show="error"><strong data-ng-bind="error"></strong></div>
</form>
</section>
Now, from this can anyone help me to add the image upload in the form and then save the field name and the image path in MongoDB.
Note that I want to reuse the upload mecanism in other forms of my app.
I had the idea of switching a generic middleware function in the road-side server wich call multer and return the image path to my sections.create function for MongoDB storing, something like that :
module.exports = function (app) {
app.route('/api/sections')
.post(uploads.upload, sections.create);
But I've never managed to pass the file in the POST from AngularJS request.
Thank you so much for all your ideas, your help and possibly an example of code that works.
I am using Angular Routing with a webapi 2 controller.
Although the default path loads with the correct data, when I click on an item in a list containing a link to a details page, no data is loaded.
The browser shows what I believe to be the correct url (http://localhost:xxxxx/#/details/2) but the DetailsController script file is not called and no method on the webapi2 controller is called.
Here is my main page :
<div class="jumbotron">
<h1>Movies Example</h1>
</div>
#section scripts {
<script src="~/Scripts/angular.js"></script>
<script src="~/Scripts/angular-route.js"></script>
<script src="~/Client/Scripts/atTheMovies.js"></script>
<script src="~/Client/Scripts/ListController.js"></script>
<script src="~/Client/Scripts/DetailsController.js"></script>
}
<div ng-app="atTheMovies">
<ng-view></ng-view>
</div>
Here is the list partial :
<div ng-controller="ListController as ctrl">
<h1>{{ctrl.message}}</h1>
<h2>There are {{ctrl.movies.length}} Movies in the Database</h2>
<table>
<tr ng-repeat="movie in ctrl.movies">
<td>{{movie.Title}}</td>
<td>
<a ng-href="/#/details/{{movie.Id}}" >Details</a>
</td>
</tr>
</table>
</div>
Here is the details partial:
<div ng-controller="DetailsController as ctrl2">
<h2>ctrl2.message</h2>
<h2>{{movie.Title}}</h2>
<div>
Released in {{movie.ReleaseYear}}
</div>
<div>
{{movie.Runtime}} minutes long.
</div>
</div>
Here is the javascript file to create the angular app:
(function () {
var app = angular.module("atTheMovies", ["ngRoute"]);
var config = function ($routeProvider) {
$routeProvider
.when("/list", { templateUrl: "/client/views/list.html" })
.when("/details/:id", { templatUrl: "/client/views/details.html" })
.otherwise(
{ redirectTo: "/list" });
};
app.config(config);
}());
Here is the javascript file to create the list controller:
(function (app) {
app.controller("ListController", ['$http', function ($http) {
var ctrl = this;
ctrl.message = "Hello World!";
ctrl.movies = [];
$http.get("/api/movie")
.success(function (data) {
ctrl.movies = data;
})
.error(function(status){
ctrl.message = status;
});
}])
}(angular.module("atTheMovies")));
Here is the javascript file to create the details controller:
(function (app) {
app.controller("DetailsController", ['$routeParams', '$http', function ($routeParams, $http) {
var ctrl2 = this;
ctrl2.message = "";
ctrl2.movie = {};
var id = $routeParams.id;
$http.get("/api/movie/" + id)
.success(function(data){
ctrl2.movie = data;
}).error(function (status) {
ctrl2.message = status;
});
}])
}(angular.module("atTheMovies")));
Finally here is the webapi2 controller
public class MovieController : ApiController
{
private MovieDb db = new MovieDb();
// GET: api/Movie
public IQueryable<Movie> GetMovie()
{
return db.Movies;
}
// GET: api/Movie/5
[ResponseType(typeof(Movie))]
public IHttpActionResult GetMovie(int id)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return NotFound();
}
return Ok(movie);
}
You have a typo in your route config i.e. templatUrl
.when("/details/:id", { templatUrl: "/client/views/details.html" })
should be
.when("/details/:id", { templateUrl: "/client/views/details.html" })
I am building a web application for our customer support. One of the needs is to be able to keep multiple tickets opened at the same time.
I was able to do the first part easily using a tabulation system and UI-Router.
However, with my current implementation, each time I change active tab, the previously-current tab is unloaded, and the now-current tab is loaded (because it was unloaded with a previous tab change).
This is not at all the expected behavior. I've already spent a couple of days trying to find a way to achieve this, without any luck.
The closest thing I was able to do is to use the multiple views system from UI-Router, but I need multiple instance of the same view to keep in memory (if multiple tickets are opened, they all are on the same view, with the same controller, but a different scope)
Here's my current implementation:
supportApp.js:
var app = angular.module("supportApp", ["ui.router", "ui.bootstrap"]);
app.config(function($stateProvider, $urlRouterProvider, $httpProvider){
$urlRouterProvider.otherwise("/");
$stateProvider
.decorator('d', function(state, parent){
state.templateUrl = generateTemplateUrl(state.self.templateUrl);
return state;
})
.state("main", {
abtract: true,
templateUrl: "main.html",
controller: "mainController"
})
.state("main.inbox", {
url: "/",
templateUrl: "inbox.html",
controller: "inboxController"
})
.state('main.viewTicket', {
url: '/ticket/{id:int}',
templateUrl: "viewTicket.html",
controller: "ticketController"
})
;
});
mainController.js: (handles other stuff, minimal code here)
app.controller("mainController", function($rootScope, $http, $scope, $state, $interval){
// Tabs system
$scope.tabs = [
{ heading: "Tickets", route:"main.inbox", active:false, params:{} }
];
var addTabDefault = {
heading: '',
route: null,
active: false,
params: null,
closeable: false
};
$rootScope.addTab = function(options){
if(!options.hasOwnProperty('route') || !options.route)
{
throw "Route is required";
}
var tabAlreadyAdded = false;
for(var i in $scope.tabs)
{
var tab = $scope.tabs[i];
if(tab.route == options.route && angular.equals(tab.params, options.params))
{
tabAlreadyAdded = true;
break;
}
}
if(!tabAlreadyAdded)
{
$scope.tabs.push($.extend({}, addTabDefault, options));
}
if(options.hasOwnProperty('active') && options.active === true)
{
$state.go(options.route, options.hasOwnProperty('params')?options.params:null);
}
};
$scope.removeTab = function($event, tab){
$event.preventDefault();
if($scope.active(tab.route, tab.params))
{
$scope.go($scope.tabs[0].route, $scope.tabs[0].params);
}
$scope.tabs.splice($scope.tabs.indexOf(tab), 1);
};
$scope.go = function(route, params){
$state.go(route, params);
};
$scope.active = function(route, params){
return $state.is(route, params);
};
$scope.$on("$stateChangeSuccess", function() {
$scope.tabs.forEach(function(tab) {
tab.active = $scope.active(tab.route, tab.params);
});
});
});
main.html:
<div class="container-fluid" id="sav-container">
<div class="row-fluid">
<div class="col-lg-2">
<form role="form" id="searchForm" action="#">
<div class="form-group has-feedback">
<input class="form-control" type="search" />
<span class="glyphicon glyphicon-search form-control-feedback"></span>
</div>
</form>
</div>
<div class="col-lg-10" id="support_main_menu">
<ul class="nav nav-tabs">
<li ng-repeat="t in tabs" ng-click="go(t.route, t.params)" ng-class="{active: t.active, closeable: t.closeable}" style="max-width: calc((100% - 128px) / {{tabs.length}});">
<a href class="nav-tab-text">
<button ng-show="t.closeable" ng-click="removeTab($event, t)" class="close" type="button">×</button>
<span>{{t.heading}}</span>
</a>
</li>
</ul>
</div>
</div>
<div class="row-fluid">
<div class="tab-content" ui-view></div>
</div>
</div>
It seems to me that what I ask is pretty standard, but I sadly couldn't find any usefull thing on the Internet
The basic idea is to store state (i.e. list of tickets) in a service as opposed to a controller. Services hang around for the life of the application. There are some articles on this. I'm still developing my approach but here is an example:
var RefereeRepository = function(resource)
{
this.resource = resource; // angular-resource
this.items = []; // cache of items i.e. tickets
this.findAll = function(reload)
{
if (!reload) return this.items;
return this.items = this.resource.findAll(); // Kicks off actual json request
};
this.findx = function(id)
{
return this.resource.find({ id: id }); // actual json query
};
this.find = function(id) // Uses local cache
{
var itemx = {};
// Needs refining
this.items.every(function(item) {
if (item.id !== id) return true;
itemx = item;
return false;
});
return itemx;
};
this.update = function(item)
{
return this.resource.update(item);
};
};
refereeComponent.factory('refereeRepository', ['$resource',
function($resource)
{
var resource =
$resource('/app_dev.php/referees/:id', { id: '#id' }, {
update: {method: 'PUT'},
findAll: {
method: 'GET' ,
isArray:true,
transformResponse: function(data)
{
var items = angular.fromJson(data);
var referees = [];
items.forEach(function(item) {
var referee = new Referee(item); // Convert json to my object
referees.push(referee);
});
return referees;
}
},
find: {
method: 'GET',
transformResponse: function(data)
{
var item = angular.fromJson(data);
return new Referee(item);
}
}
});
var refereeRepository = new RefereeRepository(resource);
// Load items when service is created
refereeRepository.findAll(true);
return refereeRepository;
}]);
So basically we made a refereeRepository service that queries the web server for a list of referees and then caches the result. The controller would then use the cache.
refereeComponent.controller('RefereeListController',
['$scope', 'refereeRepository',
function($scope, refereeRepository)
{
$scope.referees = refereeRepository.findAll();
}
]);
I have an application in angularjs where each controller is in written in different JS files.
I need to call those files only on routechange event. For now I am successful in getting the appropriate controller file, but for some reason its throwing error:
Error: [ng:areq] Argument 'ApplicantsController' is not a function, got undefined
http://errors.angularjs.org/1.2.25/ng/areq?p0=ApplicantsController&p1=not%20a%20function%2C%20got%20undefined
minErr/<#http://localhost/talentojo/app/js/angularjs/angular.js:78:5
assertArg#http://localhost/talentojo/app/js/angularjs/angular.js:1509:5
assertArgFn#http://localhost/talentojo/app/js/angularjs/angular.js:1520:76
$ControllerProvider/this.$get</<#http://localhost/talentojo/app/js/angularjs/angular.js:7278:9
nodeLinkFn/<#http://localhost/talentojo/app/js/angularjs/angular.js:6670:13
forEach#http://localhost/talentojo/app/js/angularjs/angular.js:332:11
nodeLinkFn#http://localhost/talentojo/app/js/angularjs/angular.js:6657:11
compositeLinkFn#http://localhost/talentojo/app/js/angularjs/angular.js:6105:13
publicLinkFn#http://localhost/talentojo/app/js/angularjs/angular.js:6001:30
z/<.link#https://code.angularjs.org/1.2.25/angular-route.min.js:7:388
nodeLinkFn#http://localhost/talentojo/app/js/angularjs/angular.js:6712:1
compositeLinkFn#http://localhost/talentojo/app/js/angularjs/angular.js:6105:13
publicLinkFn#http://localhost/talentojo/app/js/angularjs/angular.js:6001:30
createBoundTranscludeFn/boundTranscludeFn#http://localhost/talentojo/app/js/angularjs/angular.js:6125:1
controllersBoundTransclude#http://localhost/talentojo/app/js/angularjs/angular.js:6732:11
v#https://code.angularjs.org/1.2.25/angular-route.min.js:6:355
$RootScopeProvider/this.$get</Scope.prototype.$broadcast#http://localhost/talentojo/app/js/angularjs/angular.js:12980:15
l/<#https://code.angularjs.org/1.2.25/angular-route.min.js:11:127
qFactory/defer/deferred.promise.then/wrappedCallback#http://localhost/talentojo/app/js/angularjs/angular.js:11572:15
qFactory/defer/deferred.promise.then/wrappedCallback#http://localhost/talentojo/app/js/angularjs/angular.js:11572:15
qFactory/ref/<.then/<#http://localhost/talentojo/app/js/angularjs/angular.js:11658:11
$RootScopeProvider/this.$get</Scope.prototype.$eval#http://localhost/talentojo/app/js/angularjs/angular.js:12701:9
$RootScopeProvider/this.$get</Scope.prototype.$digest#http://localhost/talentojo/app/js/angularjs/angular.js:12513:15
$RootScopeProvider/this.$get</Scope.prototype.$apply#http://localhost/talentojo/app/js/angularjs/angular.js:12805:13
done#http://localhost/talentojo/app/js/angularjs/angular.js:8378:34
completeRequest#http://localhost/talentojo/app/js/angularjs/angular.js:8592:7
createHttpBackend/</xhr.onreadystatechange#http://localhost/talentojo/app/js/angularjs/angular.js:8535:1
My code:
HTML
<body>
<!-- Header Starts -->
<div ng-include="'assets/defaults/header.html'"></div>
<!-- Header Ends -->
<ul>
<li>
Home
</li>
<li>
Applicants
</li>
</ul>
<div ng-view></div>
<!-- Footer Starts -->
<div ng-include="'assets/defaults/footer.html'"></div>
<!-- Footer Ends -->
</body>
Route:
var app = angular.module('TOJO',['ngRoute']);
app.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/', {
templateUrl : 'assets/home.html',
controller : 'HomeController'
}).when('/applicants', {
templateUrl : 'assets/applicants/list.html',
scriptUrl : 'assets/applicants/applicants.js'
});
}).
run(function($rootScope, $location) {
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
if(next.scriptUrl !== undefined)
loadScript(next.scriptUrl);
});
});
app.controller('HomeController', function($scope) {
$scope.message = 'Look! I am home page.';
});
var loadScript = function(url, type, charset) {
if (type===undefined) type = 'text/javascript';
if (url) {
var script = document.querySelector("script[src*='"+url+"']");
if (!script) {
var heads = document.getElementsByTagName("head");
if (heads && heads.length) {
var head = heads[0];
if (head) {
script = document.createElement('script');
script.setAttribute('src', url);
script.setAttribute('type', type);
if (charset) script.setAttribute('charset', charset);
head.appendChild(script);
}
}
}
return script;
}
};
And the file applicants.js which is getting called on route change:
app.controller('ApplicantsController',['$scope', function($scope) {
$scope.message = 'Look! I am home page.';
}
]);
list.html:
<div ng-controller="ApplicantsController">{{message}}</div>
Had you already a look, into: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider resolve event?
"If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated." -> Maybe you can insert your script here into the body / head tag and return a promise.
I use this to include stylesheets e.g.
resolve: {
style: function () {
angular.element('head').append('<link href="*.css" rel="stylesheet">');
}
}
So finally I found the solution:
I used resolve, to add script in my document dynamically. So my route:
var app = angular.module('TOJO',['ngRoute']).service('HttpTojoService', Service);
app.config(function($routeProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) {
app.controllerProvider = $controllerProvider;
app.compileProvider = $compileProvider;
app.routeProvider = $routeProvider;
app.filterProvider = $filterProvider;
app.provide = $provide;
$routeProvider.when('/', {
templateUrl : 'assets/home.html',
controller : 'HomeController'
}).when('/applicants', {
templateUrl : 'assets/applicants/list.html',
controller : 'ApplicantsController',
resolve:{deps:function($q, $rootScope){
return routeResolver($q.defer(),['assets/applicants/applicants.js'],$rootScope);
}}
}).when('/jobs', {
templateUrl : 'assets/jobs/list.html',
controller : 'JobsController',
resolve:{deps:function($q, $rootScope){
return routeResolver($q.defer(),['assets/jobs/jobs.js'],$rootScope);
}}
});
});
function routeResolver(deferred,dependencies,$rootScope){
$script(dependencies, function()
{
$rootScope.$apply(function()
{
deferred.resolve();
});
});
return deferred.promise;
}
and my controller:
app.controllerProvider.register('ApplicantsController',['$scope', 'HttpTojoService', function($scope, HttpTojoService) {
$scope.message = 'Look! I am applicants page.';
}
]);
And also you'll also need scripts.js
I have a single page web app written using AngularJS. It uses PouchDB to replicate to a CouchDB server and works fine.
The problem comes when I try to convert the webpage to be available offline by adding cache.manifest. Suddenly ALL the replication tasks throw errors and stop working, whether working offline or online.
In Chrome it just says "GET ...myCouchIP/myDB/?_nonce=CxVFIwnEJeGFcyoJ net::ERR_FAILED"
In Firefox it also throws an error but mentions that the request is blocked - try enabling CORS.
CORS is enabled on the remote CouchDB as per the instructions from PouchDB setup page. Plus it works fine while not using the cache.manifest (i.e. it is quite happy with all the different ip addresses between my desk, the server and a VM - it is a prototype so there are no domain names at this time).
Incidentally, at this time I am not using any kind of authentication. Admin party is in effect.
So what changes when adding the cache.manifest? Clues gratefully welcomed.
Thanks in advance.
app.js
var app = angular.module('Assets', ['assets.controllers', 'ngRoute']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/', {
controller: 'OverviewCtrl',
templateUrl: 'views/overview.html'
}).
when('/new', {
controller: 'NewMachineCtrl',
templateUrl: 'views/machineForm.html'
}).
otherwise({redirectTo: '/'});
}]);
controller.js
var _control = angular.module('assets.controllers', ['assets.services']);
_control.controller('OverviewCtrl', ['$scope', 'Machine', function($scope, Machine) {
var promise = Machine.getAll();
promise.then(function(machineList) {
$scope.machines = machineList;
}, function(reason) {
alert('Machine list is empty: ' + reason);
});
}]);
_control.controller('UpdateMachineCtrl', ['$scope', '$routeParams', 'Machine',
function($scope, $routeParams, Machine) {
$scope.title = "Update Installation Details";
var promise = Machine.getSingle($routeParams.docId);
promise.then(function(machine) {
$scope.machine = machine;
}, function(reason) {
alert('Record could not be retrieved');
});
$scope.save = function() {
Machine.update($scope.machine);
};
}]);
_control.controller('SyncCtrl', ['$scope', 'Machine', function($scope, Machine) {
$scope.syncDb = function() {
Machine.sync();
Machine.checkConflicts();
};
$scope.checkCors = function() {
// Check CORS is supported
var corsCheck = function(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// CORS not supported.
console.log('CORS not supported by browser');
}
xhr.onload = function() {
console.log('Response from CORS ' + method + ' request to ' + url + ': ' + xhr.responseText);
};
xhr.onerror = function() {
console.log('Error response from CORS ' + method + ' request to ' + url + ': ' + xhr.responseText);
};
xhr.send();
};
var server = 'http://10.100.3.21:5984/ass_support';
corsCheck('GET', server);
corsCheck('PUT', server);
corsCheck('POST', server);
corsCheck('HEAD', server);
// corsCheck('DELETE', server);
};
}]);
service.js
var _service = angular.module('assets.services', []);
_service.constant('dbConfig',{
dbName: 'assets',
dbServer: 'http://myCouchServerIp:5984/'
});
/**
* Make PouchDB available in AngularJS.
*/
_service.factory('$db', ['dbConfig', function(dbConfig) {
PouchDB.enableAllDbs = true;
var localDb = new PouchDB(dbConfig.dbName);
var remoteDb = dbConfig.dbServer + dbConfig.dbName;
var options = {live: true};
var syncError = function() {
console.log('Problem encountered during database synchronisation');
};
console.log('Replicating from local to server');
localDb.replicate.to(remoteDb, options, syncError);
console.log('Replicating from server back to local');
localDb.replicate.from(remoteDb, options, syncError);
return localDb;
}]);
_service.factory('Machine', ['$q', '$db', '$rootScope', 'dbConfig',
function($q, $db, $rootScope, dbConfig) {
return {
update: function(machine) {
var delay = $q.defer();
var doc = {
_id: machine._id,
_rev: machine._rev,
type: machine.type,
customer: machine.customer,
factory: machine.factory,
lineId: machine.lineId,
plcVersion: machine.plcVersion,
dateCreated: machine.dateCreated,
lastUpdated: new Date().toUTCString()
};
$db.put(doc, function(error, response) {
$rootScope.$apply(function() {
if (error) {
console.log('Update failed: ');
console.log(error);
delay.reject(error);
} else {
console.log('Update succeeded: ');
console.log(response);
delay.resolve(response);
}
});
});
return delay.promise;
},
getAll: function() {
var delay = $q.defer();
var map = function(doc) {
if (doc.type === 'machine') {
emit([doc.customer, doc.factory],
{
_id: doc._id,
customer: doc.customer,
factory: doc.factory,
lineId: doc.lineId,
plcVersion: doc.plcVersion,
}
);
}
};
$db.query({map: map}, function(error, response) {
$rootScope.$apply(function() {
if (error) {
delay.reject(error);
} else {
console.log('Query retrieved ' + response.rows.length + ' rows');
var queryResults = [];
// Create an array from the response
response.rows.forEach(function(row) {
queryResults.push(row.value);
});
delay.resolve(queryResults);
}
});
});
return delay.promise;
},
sync: function() {
var remoteDb = dbConfig.dbServer + dbConfig.dbName;
var options = {live: true};
var syncError = function(error, changes) {
console.log('Problem encountered during database synchronisation');
console.log(error);
console.log(changes);
};
var syncSuccess = function(error, changes) {
console.log('Sync success');
console.log(error);
console.log(changes);
};
console.log('Replicating from local to server');
$db.replicate.to(remoteDb, options, syncError).
on('error', syncError).
on('complete', syncSuccess);
console.log('Replicating from server back to local');
$db.replicate.from(remoteDb, options, syncError);
}
};
}]);
_service.factory('dbListener', ['$rootScope', '$db', function($rootScope, $db) {
console.log('Registering a onChange listener');
$db.info(function(error, response) {
$db.changes({
since: response.update_seq,
live: true,
}).on('change', function() {
console.log('Change detected by the dbListener');
// TODO work out why this never happens
});
});
}]);
cache.manifest
CACHE MANIFEST
# views
views/machineForm.html
views/overview.html
# scripts
scripts/vendor/pouchdb-2.2.0.min.js
scripts/vendor/angular-1.2.16.min.js
scripts/vendor/angular-route-1.2.16.min.js
scripts/app.js
scripts/controllers/controller.js
scripts/services/service.js
index.html
<!DOCTYPE html>
<html lang="en" manifest="cache.manifest" data-ng-app="Assets">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Asset Management</title>
<script src="scripts/vendor/angular-1.2.16.min.js" type="text/javascript"></script>
<script src="scripts/vendor/angular-route-1.2.16.min.js" type="text/javascript></script>
<script src="scripts/vendor/pouchdb-2.2.0.min.js" type="text/javascript"></script>
<script src="scripts/app.js" type="text/javascript"></script>
<script src="scripts/services/service.js" type="text/javascript"></script>
<script src="scripts/controllers/controller.js" type="text/javascript"></script>
</head>
<body>
<div id="content">
<nav class="sidebar">
<h3>Options</h3>
<div>
<a class="active" data-ng-href="#/">Overview</a>
<a data-ng-href="#" data-ng-controller="SyncCtrl" data-ng-click="syncDb()">Synchronise</a>
<a data-ng-href="" data-ng-controller="SyncCtrl" data-ng-click="checkCors()">Check CORS</a>
</div>
</nav>
<section class="main">
<div data-ng-view></div>
</section>
</div>
</body>
</html>
overview.html
<h3>Installation Overview</h3>
<table>
<tr>
<th>Customer</th>
<th>Factory</th>
<th>Line Id</th>
<th>PLC Version</th>
</tr>
<tr data-ng-repeat="machine in machines">
<td>{{machine.customer}}</td>
<td>{{machine.factory}}</td>
<td><a data-ng-href="#/view/{{machine._id}}">{{machine.lineId}}</a></td>
<td>{{machine.plcVersion}}</td>
</tr>
</table>
machineForm.html
<h3>{{title}}</h3>
<form name="machineForm" data-ng-submit="save()">
<div>
<label for="customer">Customer:</label>
<div><input data-ng-model="machine.customer" id="customer" required></div>
</div>
<div>
<label for="factory">Factory:</label>
<div><input data-ng-model="machine.factory" id="factory" required></div>
</div>
<div>
<label for="lineId">Line ID:</label>
<div><input data-ng-model="machine.lineId" id="lineId" required></div>
</div>
<div>
<label for="plcVersion">PLC Version:</label>
<div><input data-ng-model="machine.plcVersion" id="plcVersion"></div>
</div>
<div><button data-ng-disabled="machineForm.$invalid">Save</button></div>
</form>
Try changing your cache.manifest file to this:
CACHE MANIFEST
CACHE:
# views
views/machineForm.html
views/overview.html
# scripts
scripts/vendor/pouchdb-2.2.0.min.js
scripts/vendor/angular-1.2.16.min.js
scripts/vendor/angular-route-1.2.16.min.js
scripts/app.js
scripts/controllers/controller.js
scripts/services/service.js
NETWORK:
*
When using a manifest file, all non-cached resources will fail on a cached page, even when you're online. The NETWORK section tells the browser to allow requests to non-cached resources (they'll still fail while offline, of course).