I have a loading div that I'd like to share across several controllers. Is there a way to accomplish this without placing this template in every other template?
For example:
<div ng-show="loading" class="loading">Loading</div>
Now in my controller I turn this off and on my using $scope.loading = true/false.
In my main page I use this:
<div class="container" ng-app="myApp">
<div ng-view>
</div>
</div>
I'm using routing so, right now I have to place the loading div in each template that is called by the router so it is inserted in ng-view. All I want is one location for the loading div. How do I accomplish this?
Set up the loading div as a custom directive, check out this example from the angularjs documentation:
angular.module('docsSimpleDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
template: 'Name: {{customer.name}} Address: {{customer.address}}'
};
});
http://plnkr.co/edit/?p=preview
There's quite a few ways to do this.
What I've come up with is to use a service to register an AppController callback. Then in each of my page's controllers I inject that service, and call the callback with true/false.
The full code is below, but the main lines to focus on is:
myApp.service('LoadingService', function() {
var controllerCallback = function() {};
this.setControllerCallback = function(callback) {
controllerCallback = callback;
};
this.setLoading = function(bool) {
controllerCallback(bool);
};
});
And in your AppController:
LoadingService.setControllerCallback(function(bool) {
$scope.loading = bool;
});
Then to show the loading div, just inject that service into a controller and call
LoadingService.setLoading(true) //or false
This allows for the service to be reused in any controller that needs to toggle the loading div.
// Code goes here
var myApp = angular.module('app', ['ngRoute']);
myApp.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/page1', {
templateUrl: 'page1.html',
controller: 'PageOneController'
})
.when('/page2', {
templateUrl: 'page2.html',
controller: 'PageTwoController'
})
});
myApp.controller('AppController', function($scope, LoadingService) {
$scope.loading = false;
LoadingService.setControllerCallback(function(bool) {
$scope.loading = bool;
});
});
myApp.controller('PageOneController', function($timeout, LoadingService) {
LoadingService.setLoading(true);
$timeout(function() {
LoadingService.setLoading(false);
}, 2000);
});
myApp.controller('PageTwoController', function($timeout, LoadingService) {
LoadingService.setLoading(true);
$timeout(function() {
LoadingService.setLoading(false);
}, 3000);
});
myApp.service('LoadingService', function() {
var controllerCallback = function() {};
this.setControllerCallback = function(callback) {
controllerCallback = callback;
};
this.setLoading = function(bool) {
controllerCallback(bool);
};
});
<!DOCTYPE html>
<html ng-app="app">
<head>
</head>
<body ng-controller="AppController">
<h1>Title</h1>
Page 1
Page 2
<div ng-if="loading">Loading...</div>
<div ng-view></div>
<script src="https://code.angularjs.org/1.4.3/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular-route.min.js"></script>
<script type="text/ng-template" id="page1.html">
<div>You're on page one</div>
</script>
<script type="text/ng-template" id="page2.html">
<div>You're on page two</div>
</script>
</body>
</html>
Related
In my project I am using angular $routeProvider as page navigation. In order to go back I'm using javascript.go(-1). The problem is when the button back was clicked, it's loading and rendering again all the data. Is it possible to save the previous stage in javascript.go(-1)?
Example:
app.config(function ($routeProvider, localStorageServiceProvider) {
$routeProvider
.when('/api/lecturer/', {
templateUrl: 'partials/dashboard.html',
controller: 'dashboardController'
})
.when('/account/lecturer/project/', {
templateUrl: 'part/lecturer_project.html',
controller: 'projectController'
}).otherwise({redirectTo: '/login'});})
HTML:
<li>
<a onclick="javascript:history.go(-1);" style="cursor:pointer;" class="button">back
<i class="entypo-left-dir right"></i>
</a>
</li>
For a simple implementation, you need to remember the previous page (only one), and store it in some service/factory that will provide the stored value to your controllers. Here is a simple demo with ngRoute:
var app = angular.module("myApp", ["ngRoute"]);
app.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: "main.html"
})
.when('/api/lecturer/', {
templateUrl: 'partials/dashboard.html',
controller: 'dashboardController'
})
.when('/account/lecturer/project/', {
templateUrl: 'part/lecturer_project.html',
controller: 'projectController'
})
.otherwise({
redirectTo: '/'
})
});
app.controller('dashboardController', function($scope, historyService) {
$scope.back = historyService.get();
});
app.controller('projectController', function($scope, historyService) {
$scope.back = historyService.get();
});
app.service('historyService', function() {
/* `this.back` can be an array instead,
which will work as a stack,
pushing new previous pages
and popping then when redirected back
*/
this.back = "";
this.get = function() {
return this.back;
}
this.set = function(val) {
this.back = val;
}
this.delete = function() {
this.back = "";
}
})
app.run(function($rootScope, historyService) {
// `$locationChangeStart` event works better for this example
$rootScope.$on("$locationChangeStart", function(event, next, prev) {
//console.log(event);
//console.log(next); // current page
//console.log(prev); // previous page
historyService.set(prev); // store the previous page in a service
});
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-route.js"></script>
<body ng-app="myApp">
lecturer
project
<hr>
<div ng-view></div>
<script type="text/ng-template" id="main.html">
<p>main.html</p>
</script>
<script type="text/ng-template" id="partials/dashboard.html">
<p>partials/dashboard.html</p>
<a ng-href="#!/">Main</a>
<a ng-href="{{back}}">Go back</a>
</script>
<script type="text/ng-template" id="part/lecturer_project.html">
<p>part/lecturer_project.html</p>
<a ng-href="#!/">Main</a>
<a ng-href="{{back}}">Go back</a>
</script>
</body>
</html>
I have a question about how to pass scope to a template rendered from a directive. This seems like it should be so straight forward but somehow I am having a lot of trouble getting it to work.
My HTML (simplified for brevity) is as follows:
<div ng-app="myApp">
<md-content>
<!-- strangely TestController as tc doesnt work -->
<div ng-controller="TestController">
<div ng-click="showDialog();">Show</div> <!-- this also doesnt seem to work.. -->
</div>
</md-content>
</div>
And then the application (contained in a script tag on index.html):
// app -- using components
var app = angular.module('ctrlApp',
['components', 'ngMaterial']);
// removed app.config from ngMaterial for brevity
The app contains a controller that has a function to show a mdDialog:
//controller
app.controller('TestController', function($scope, $log, $mdDialog) {
$scope.items = [{'title': 1},{'title': 2}];
// open modal
$scope.showDialog = function() {
$mdDialog.show({
templateUrl: 'dialog.html',
parent: angular.element(document.body),
clickOutsideToClose:true,
controller: function() {
// scope from parent persists here as expected
console.dir($scope.items);
// used to wire up dialog specific UI behavior
$scope.cancel = function() { $mdDialog.hide(); }
}
});
};
});
Dialog.html just renders out the directive in a modal:
<md-dialog aria-label="test">
<form ng-cloak>
<!-- rendering a directive here -->
<my-directive></my-directive>
</form>
</md-dialog>
And finally, returning back to the app, here is the directive:
// link up a directive that is rendered in the model form
var d = angular.module('components', []);
d.directive('myDirective', function() {
function link(scope, element, attributes ) {
console.log("scope.items are ", scope.items); }
return({
restrict: "E",
controller: 'TestController',
link: link,
templateUrl: 'directive.html',
});
});
And finally the directive.html template:
<div>
<h1>my Directive template</h1>
<pre> Empty!: {scope.items}</pre>
</div>
I am confused as to how I can get the scope that is created by the TestController into the template that is rendered by the directive. It works fine all the way up to the 'link' function but not in the directive's template...
Any advice would be much appreciated!
Thanks
- X
You have done mistake in directive.html,
Your code:
<div>
<h1>my Directive template</h1>
<pre> Empty!: {scope.items}</pre>
</div>
Expected code:
<div>
<h1>my Directive template</h1>
<pre> Empty!: {{scope.items[0]}}</pre> //Here you need to pass Array index
</div>
Here i given you running code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="bower_components/angular-material/angular-material.min.css" media="screen" title="no title" charset="utf-8">
<script src="bower_components/angular/angular.min.js" charset="utf-8"></script>
<script src="bower_components/angular-animate/angular-animate.min.js" charset="utf-8"></script>
<script src="bower_components/angular-aria/angular-aria.min.js" charset="utf-8"></script>
<script src="bower_components/angular-messages/angular-messages.min.js" charset="utf-8"></script>
<script src="bower_components/angular-material/angular-material.js" charset="utf-8"></script>
</head>
<body>
<div ng-app="ctrlApp">
<md-content>
<!-- strangely TestController as tc doesnt work -->
<div ng-controller="TestController">
<div ng-click="showDialog();">Show</div>
<!-- this also doesnt seem to work.. -->
</div>
</md-content>
</div>
<script type="text/javascript">
/** Own Module **/
var d = angular.module('components', []);
d.directive('myDirective', function() {
function link(scope, element, attributes) {
console.log("scope.items are ", scope.items);
}
return ({
restrict: "E",
controller: 'TestController',
link: link,
template: '<div>\
<h1>my Directive template</h1>\
<pre> Empty!: {{items[0]}}</pre>\
</div>',
});
});
var app = angular.module('ctrlApp', ['components', 'ngMaterial']);
app.controller('TestController', function($scope, $log, $mdDialog) {
$scope.items = [{
'title': 1
}, {
'title': 2
}];
// open modal
$scope.showDialog = function() {
$mdDialog.show({
template: '<md-dialog aria-label="test">\
<form ng-cloak>\
<my-directive></my-directive> </form>\
</md-dialog>',
parent: angular.element(document.body),
clickOutsideToClose: true,
controller: function() {
// scope from parent persists here as expected
console.dir($scope.items);
// used to wire up dialog specific UI behavior
$scope.cancel = function() {
$mdDialog.hide();
}
}
});
};
});
</script>
</body>
</html>
If you want to pass the scope to template try something like this
In Dialog.html
In Directive just add the following line
return({
restrict: "E",
controller: 'TestController',
scope:{
item:'='
},
link: link,
templateUrl: 'directive.html',
});
Hope this helps.
If you want to pass scope to $mdDialog look at the link below.
https://github.com/angular/material/issues/1531
I am trying to exchange data between two different controllers. I will access data in different controllers. I use /loading for showing a spinner. While navigating between different pages I load this spinner and after some time delay I navigate to another url which is intended. So I use this service to store the uri.
I have the following service in my angular js app.
myApp.service('myService', function() {
var data = "";
return {
getUri: function () {
return data;
},
setUri: function (uri) {
data = uri;
}
};
});
The following are my routes:
twitter.config([ '$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider, $routeParams) {
$routeProvider.when('/', {
templateUrl : 'main.html',
controller : 'mainController'
}).when('/loading', {
templateUrl : 'spinner.html',
controller : 'spinnerController'
}).when('/login', {
templateUrl : 'login.html',
controller : 'loginController'
}).when('/signup', {
templateUrl : 'signup.html',
controller : 'signupController'
});
$locationProvider.html5Mode(true);
} ]);
so I am trying to put data into the service by doing
myService.set('mydata');
and getting data by
myService.get();
But every time when I try to access the service in different controllers defined above I get empty string.
your service public methods are getUri and setUri but you are trying to use as myservice.get() and myservic.set().check the below snippet
var myApp = angular.module('myApp', []);
myApp.controller('controller1', function($scope, myService) {
$scope.ControllerData = 'From Controller 1';
myService.setUri('www.google.com');
});
myApp.controller('controller2', function($scope, myService) {
$scope.ControllerData = 'From Controller 2';
$scope.sharedData = myService.getUri();
});
myApp.service('myService', function() {
var data = "";
return {
getUri: function() {
return data;
},
setUri: function(uri) {
data = uri;
}
};
});
<!DOCTYPE html>
<html ng-app="myApp">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
<meta charset=utf-8 />
<title>ng-click</title>
</head>
<body>
<div ng-controller="controller1">
Controller1 Data: {{ControllerData}}
</div>
<div ng-controller="controller2">
Controller 2 Data:{{ControllerData}}
<br>Shared Data :{{sharedData}}
</div>
</body>
</html>
Try to set in this way :
setUri: function (uri) {
this.data = uri;
}
I am relatively new to angularJS, I am trying to set up a page where inturn multiple pages are called depending upon the selection made previously.
All the pages have their own controller, so I am trying to set the controller and view src through the javascript and using them in HTML tags.
Following is what I am doing:
HTML page:
<div ng-if="sidebarName=='sidebar-device-wire'">
<div ng-controller="getSidebarCtlr">
<div ng-include src="sidebarSrc"></div>
</div>
</div>
javascript:
$scope.sidebarSrc="views/sidebars/sidebar-device.html";
$scope.sidebarCtlr="SidebarDeviceCtrl";
$scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
For some reason though, this does not work. i can get the HTML page but the controller is not being called. Can anyone please tell me what I am doing wrong?
I would also recommend to use ngRoute or ui.router because there are many features that aren't easy to implement from scratch (like named views, nested views / nested states or resolves) and these modules are well tested.
Not sure why your controller isn't running but I guess that the expression of the controller is evaluated before your controller that is setting the name is running. So it will be always undefined at compile time.
But if you really like to implement a very basic router you could do it like in the following demo (or in this fiddle).
Update 21.12.2015
Here are some router examples that I wrote for other SO questions:
simple ui.router example - jsfiddle
more complex nested state example ui.router - jsfiddle
dynamic link list with ngRoute - jsfiddle
Please also have a look at ui.router github pages to learn more about it.
angular.module('simpleRouter', [])
.directive('simpleView', simpleViewDirective)
.provider('simpleRoutes', SimpleRoutesProvider)
.controller('MainController', MainController)
.controller('HomeController', HomeController)
.config(function(simpleRoutesProvider) {
simpleRoutesProvider.state([{
url: '/',
templateUrl: 'home.html',
controller: 'HomeController'
}, {
url: '/view1',
templateUrl: 'view1.html'
}, {
url: '/view2',
templateUrl: 'view2.html',
controller: function($scope) {
$scope.test = 'hello from controller'
}
}]);
simpleRoutesProvider.otherwise('/');
})
function HomeController($scope) {
$scope.hello = 'hello from home controller!!';
console.log('home controller started')
}
function MainController($scope) {
$scope.hello = 'Main controller test';
}
function simpleViewDirective() {
return {
restrict: 'EA',
scope: {},
template: '<div ng-include="templateUrl"></div>',
controller: function($scope, $location, $controller, simpleRoutes) {
var childControllerInst;
$scope.templateUrl = simpleRoutes.currentRoute.templateUrl || simpleRoutes.otherwise.templateUrl;
$scope.$watch(function() {
return $location.path();
}, function(newUrl) {
//console.log(newUrl)
$scope.templateUrl = simpleRoutes.changeRoute(newUrl);
childControllerInst = $controller(simpleRoutes.currentRoute.controller || function() {}, {$scope: $scope});
});
$scope.$on('$destroy', function() {
childControllerInst = undefined;
})
},
link: function(scope, element, attrs) {
}
}
}
function SimpleRoutesProvider() {
var router = {
currentRoute: {
templateUrl: ''
},
states: [],
otherwise: {},
changeRoute: function(url) {
var found = false;
angular.forEach(router.states, function(state) {
//console.log('state', state);
if (state.url == url) {
router.currentRoute = state;
found = true;
}
});
if (!found) router.currentRoute = router.otherwise;
//console.log(router.currentRoute);
return router.currentRoute.templateUrl;
}
};
this.state = function(stateObj) {
router.states = stateObj;
};
this.otherwise = function(route) {
angular.forEach(router.states, function(state) {
if (route === state.url ) {
router.otherwise = state;
}
});
//console.log(router.otherwise);
};
this.$get = function simpleRoutesFactory() {
return router;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="simpleRouter" ng-controller="MainController">
<script type="text/ng-template" id="home.html">home route {{hello}}</script>
<script type="text/ng-template" id="view1.html">view1</script>
<script type="text/ng-template" id="view2.html">view2 {{test}}</script>
<div simple-view="">
</div>
home
view1
view2
<br/>
{{hello}}
</div>
What's that code means? $scope.getSidebarCtlr = function(){return $scope.sidebarCtlr;}
the ng-directive requires a Controller name, its argument type is string and you cannot pass a simple function, you need to register a valid controller associating it to a module via the controller recipe.
https://docs.angularjs.org/guide/controller
angular.module('test', []).controller('TestCtrl', function($scope) {
$scope.greetings = "Hello World";
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<section ng-app="test">
<article ng-controller="TestCtrl">{{ greetings }}</article>
</section>
So i still new to angular and java script but i read it was good practical to wrap the everything into functions.
This is what i have done.
in comment is the previous version
app.js
//var app = angular.module('app',['ngRoute','ngResource','routes']);
(function(angular) {
angular.module("app.directives", []);
angular.module("app.controllers", []);
angular.module("app", ['ngRoute','ngResource','routes','app.directives','app.controllers']);
}(angular));
directives.js
/*
app.directive('footer', function () {
return {
replace: true,
templateUrl: "/template/main_footer.html",
controller: ['$scope', '$filter', function ($scope, $filter) {
}]
}
});
*/
(function(angular) {
var footer = function () {
return {
replace: true,
templateUrl: "/template/main_footer.html",
controller: ['$scope', '$filter', function ($scope, $filter) {
}]
};
};
footer.$inject = [];
angular.module("app.directives").controller("footer", footer);
});
controller.js
/*app.controller('HeaderController',function($scope, $location) {
$scope.isActive = function (viewLocation) {
var active = (viewLocation === $location.path());
return active;
};
});*/
(function(angular) {
var HeaderController = function($scope,$location){
$scope.isActive = function(viewLocation){
var active = (viewLocation === $location.path());
return active;
};
}
HeaderController.$inject = ['$scope','$location'];
angular.module("app.controllers").controller("HeaderController", HeaderController);
})
and how should i proceed for routes.js
angular.module('routes', []).config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'pages/home.html'
})
.when('/second', {
templateUrl: 'pages/second.html'
})
.when('/third', {
templateUrl: 'pages/third.html'
})
.when('/123', {
templateUrl: 'pages/123.html'
})
.otherwise({
redirectTo: '/'
});
});
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/custom.css">
<script type="text/javascript" src="https://code.angularjs.org/1.4.4/angular.js"></script>
<script type="text/javascript" src="https://code.angularjs.org/1.4.4/angular-resource.js"></script>
<script type="text/javascript" src="https://code.angularjs.org/1.4.4/angular-route.js"></script>
<script type="text/javascript" src="js/routes.js"></script>
<script type="text/javascript" src="js/app.js"></script>
<script type="text/javascript" src="js/controllers.js"></script>
<script type="text/javascript" src="js/directives.js"></script>
</head>
<body>
<div ng-view></div>
</body>
</html>
But it does not work. And i find it hard to find what went wrong because i don't get a error with the development tools on google chrome
Update
Now i do call the function at the end.
But i still can't see the footer. I also added the footer.html just in case there would something i forgot there.
directives.js
(function(angular) {
var footer = function () {
return {
replace: true,
templateUrl: "/template/main_footer.html",
controller: ['$scope', '$filter', function ($scope, $filter) {
}]
};
};
footer.$inject = [];
angular.module("app.directives").controller("footer", footer);
}(angular));
home.html
<div>
<div>
<h3>This is the homepage</h3>
</div>
<div footer></div>
</div>
In the 'directives.js' and 'controller.js' files, you forgot (angular) at the end, like in 'app.js'.
If you write a function like this:
function f() {
// do stuff...
}
it's never going to run unless you call it somewhere: f();. Now, if you want to wrap a bit of code in a function, you still want it to run immediately as if it had not been wrapped. So what you do is you call the wrapping function immediately like this:
(function f() {
// do stuff...
})();
or like this (same thing):
(function f() {
// do stuff...
}());
In that second way of writing things, the outermost parentheses are useless for the program but they help the reader see that the function will be immediately run (or "evaluated", or "called").
You can also pass anything that lives outside of the function into the function, for example angular:
(function f(angular) {
// do stuff...
}(angular));
That is because you're missing the function invocation in some of those wrappings. For example, controller.js needs to be:
(function(angular) {
var HeaderController = function($scope,$location){
$scope.isActive = function(viewLocation){
var active = (viewLocation === $location.path());
return active;
};
}
HeaderController.$inject = ['$scope','$location'];
angular.module("app.controllers").controller("HeaderController", HeaderController);
})(angular); // CALL FUNCTION
Note in the last line I added (angular); to actually call the function.