How do i can run directive function before controller? - javascript

I need call directive function ( i need scope ) before controller.
var app = angular.module('docsRestrictDirective', []);
app.controller('Controller', ['$scope', function($scope ) {
$scope.changeDerictive();
}]);
app.directive('ngMyDer', function() {
return {
restrict: 'A',
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) {
scope.changeDerictive = function() {
console.log("changed");
};
}
}
}
}
});
http://plnkr.co/edit/NWb23rScg8zvPluGBWH5?p=preview

as requested this is the example with ui-router.
first we will define a controller for the base of the app.
<body ng-app="myApp" ng-controller="AppBaseCtrl">
<main role="main">
<overlay-spinner></overlay-spinner>
<invite-success></invite-success>
<div ui-view></div>
</main>
</body>
now in the ui router we will define our base route:
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/desiredRoute');
$stateProvider
.state('desiredRoute', {
url: '/desiredRoute',
templateUrl: 'views/pathToTemplate.html',
controller: 'MyViewCtrl'
})
});
so what will happen? the base controller runs, we can initialize desired scope variables, then we run the directives and then we run our required controller.
so you have the directive which runs before the needed controller.
if we want this to be cleaner with ui-router we can define the routes like this:
in the routes config:
.state('dashboard', {
url: '/dashboard',
templateUrl: 'views/templates/dashboard/dashboard-base.html',
controller: 'DashboardBaseCtrl',
abstract: true
})
.state('dashboard.main', {
url: '/main',
templateUrl: 'views/templates/dashboard/dashboard-main.html',
controller: 'DashboardMainCtrl'
})
then in the view for the dashboard-base:
<div myDirective></div>
<div ui-view></div>
and of course define in the base controller what ever you want and as you can see... base controller runs then directive then our desired controller so directive runs before the controller...
EDIT
I have created a small plunker like you asked... you will see here that with no timeout the directive is called before our main controller does using a base controller my example is the first example in the answer
plunker with answer

Your ng-controller is written before your directive ,so if you want to call directive frist write ng-controller after your directive
like this
<body ng-app="docsRestrictDirective">
<div ></div>
<div ng-my-der></div>
<div ng-controller="Controller"></div>
</body>
Then it will show result according to you
Plunker: http://plnkr.co/edit/0qccTyPADwDaq05KKmao?p=preview

There is a priority between angularjs directives.ng-controller is directive too,try that priority.Maybe it can help you.

Thanks for answers.
I did it this way
var app = angular.module('app', []);
app.controller( "Ctrl",[ "$scope", function ( $scope ) {
$scope.$watch ( "ngExchange", function ( ) {
$scope._ngExchange[0].remove()
$scope._ngExchange[1].after( $scope._ngExchange[0] );
} );
} ] );
app.directive('ngExchange', function(){
return {
restrict: 'A',
controller: function ( $scope, $element, $attrs ) {
if ( $scope._ngExchange === undefined ) {
$scope._ngExchange = [];
}
$scope._ngExchange.push( $element );
}
}
});
http://plnkr.co/edit/fagINqNafPp6vEhawNbl?p=preview

Related

Animation in AngularJS Directive, event not firing

I'm trying to do something after an 'enter' event in a directive. The event isn't firing when the template is loaded in.
Here is the app declaration
angular
.module('MyApp', [
'ngAnimate',
'ngCookies',
'ngResource',
'ngRoute',
'ngSanitize',
'ngTouch'
])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
})
.when('/in-the-community', {
templateUrl: 'views/in-the-community.html',
controller: 'CommunityCtrl'
})
.otherwise({
redirectTo: '/'
});
});
Initially I am using the routing provider to give me a template page. I am then trying to use a directive inside these templates to provide another view. This works by using the following in my template page
<div class="flex">
<div class="fc fci image-wrapper" id="home-banner">
</div>
<div class="fc fcc">
<section show-and-hide-content="{{ sub_content }}">
</section>
</div>
</div>
This loads in the following directive
angular.module('rubisApp')
.directive('showAndHideContent', function ($animate) {
return {
templateUrl: 'views/community-sub.html',
controller: 'CommunitySubCtrl',
link: function(scope, element, attributes) {
$animate.on('enter', element,
function callback(element, phase) {
console.log('attributes.showAndHideContent');
}
);
}
};
});
The console log isn't running and I can only presume that is because it isn't firing the $animate.on event.
Does angular apply the ng-enter class to a templateUrl in a directive?
I'm pretty new to angular so if this is the wrong way of doing this, an alternative would really help also.
The $animate dependency as not being pulled into the directive.
angular.module('MyApp')
.directive('showAndHideContent',['$animate', function ($animate) {
return {
templateUrl: 'views/community-sub.html',
controller: 'CommunitySubCtrl',
link: function(scope, element, attributes) {
$animate.on('enter', element,
function callback(element, phase) {
console.log('attributes.showAndHideContent');
}
);
}
};
}]);
i see you are using an controller. can't you create the event there?
I checked my code and have this snippet on controller, to destroy an interval when route changes, notice that i use $on and '$destroy', with $. can it be that?
$scope.$on('$destroy', function () {
$interval.cancel($scope.interval);
});

AngularJS ui-view not receiving scope

I want to simply show a sidebar with ng-if when isCreating === true. The ng-click event is from a nmNav directive which is bound to the same controller as the state – NotesController.
But it seems that I'm not managing to pass the scope to the ui-view, though, I have stated that the controller is NotesController.
What I mean by this is that isCreating becomes true on the scope outside of ui-view when startCreating() is called.
▶︎ When setting the variables to the $rootScope everything works
What am I doing wrong?
Here's all relevant code...
notesMan-app.js
angular.module('NoteMan', [
'ui.router',
'ngAnimate',
'notes'
])
.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider
.state('notes', {
url: '/',
templateUrl: 'app/components/notes/notes.tmpl.html',
controller: 'NotesController',
controllerAs: 'notesCtrl',
})
.state('note', {
url: '/note/:title',
templateUrl: 'app/components/notes/singleNote/notes-singleNote.tmpl.html',
controller: 'NotesController',
controllerAs: 'notesCtrl',
params: {
title: null
}
});
$urlRouterProvider.otherwise('/'); // when no routematch found redirect to /view1
$locationProvider.html5Mode(true);
})
.controller('NotesController', function ($scope, $stateParams, $state, $http) {
var vm = this;
$scope.isEditing = false;
$scope.isCreating = false;
function showCreating() {
return $scope.isCreating && !$scope.isEditing;
}
function startCreating() {
$scope.isCreating = true;
$scope.isEditing = false;
showCreating();
}
//Define methods
$scope.startCreating = startCreating;
//Get notes from an external file
$http.get('./app/API/notes.json').then(function (res) {
vm.notes = res.data;
});
});
menu.js
angular.module('NoteMan')
.directive('nmNav', function() {
return {
replace:true,
restrict: 'E',
templateUrl: 'app/common/menu/menu.tmpl.html',
controller: 'NotesController as notesCtrl',
link: function (scope, elem, attr){
console.log(scope);
}
};
});
I cant figure out call to "startCreating" in your code.
Below are my guess
a-either you have not specified the controller as "NotesController" to the container element where you are calling method "startCreating"
b-Or you have mispelled the name of controller "NotesController"or method "startCreating"
c-Or you have missed prefixing $scope to call to method "startCreating"
please check and confirm

AngularJS ng-controller directive does not accept variable (scope function) from javascript, does not give any error either

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>

AngularJS dynamically set class on <html> tag based on route

I'm not sure the best way to approach this.
I want to dynamically set a class on my /login route so that my login page can have a large background image.
What is the best way to approach this?
Here's my current code:
<!DOCTYPE html>
<html class="SOME_DYNAMIC_CLASS_HERE_BASED_ON_ROUTE">
...
</html>
<body ng-app="myApp">
<div ng-view=""></div>
</body>
angular.module('myApp', ['ngRoute']).config(function ($routeProvider) {
$routeProvider
.when('/login', {
templateUrl: 'login.html',
controller: 'LoginCtrl'
})
.when('/', {
templateUrl: 'dashboard.html',
controller: 'DashboardCtrl'
})
You must have your ng-app attached in the <html> element, to have any sort of connection between angular and the view. Since you want something to change base on the current route of your application, then why not use those routes as a reference for your configuration, e.g. the $routeProvider configuration. Attach all your configuration, including configuration from classes to styles or any other configuration within the route object. You can then create a directive that listens to route changes via $routeChangeSuccess and then get the current route and other properties using the $route object defined as the second parameter of the $routeChangeSuccess listener, once you have those properties, you can do whatever you want with it e.g. append a class to that directive element.
DEMO
Javascript
Configuration
.config(function ($routeProvider) {
$routeProvider
.when('/dashboard', {
templateUrl: 'dashboard.html',
'class': 'bg-dashboard'
})
.when('/login', {
templateUrl: 'login.html',
'class': 'bg-login'
})
.otherwise({redirectTo: '/login'});
});
Directive
.directive('classRoute', function($rootScope, $route) {
return function(scope, elem, attr) {
var previous = '';
$rootScope.$on('$routeChangeSuccess', function(event, currentRoute) {
var route = currentRoute.$$route;
if(route) {
var cls = route['class'];
if(previous) {
attr.$removeClass(previous);
}
if(cls) {
previous = cls;
attr.$addClass(cls);
}
}
});
};
});
HTML
<html ng-app="myApp" class-route>...</html>
Using a directive is one way to go.
.directive("routeClass", function($location, $parse) {
var mapping = {};
return {
restrict: "A",
scope: {},
link: function(scope, element, attrs) {
mapping = $parse(attrs["routeClass"])(scope);
// do something with mapping and $location or $routeParams
}
}
});
<any route-class="{'/': 'default', '/Book': 'book'}" />
Another - is to set it via $rootScope.
I know this is an old thread, but I came across it looking for some pointers. I have gone for the following method which feels more "Angular". Note, it is using a controllerAs-based directive:
ngModule.directive('routeClass', routeClass);
function routeClass($state, $log) {
function controllerFn($scope) {
var routeClass = this;
$scope.$on('$stateChangeSuccess', function(){
// I have a different class name assignment as the
// setup of my states is relatively complex,
// but this should demonstrate the idea
routeClass.current = 'page-' + $state.current.name;
});
}
return {
controller: controllerFn,
controllerAs: 'routeClass',
restrict: 'A'
};
}
And it is used as follows in index.html:
<body ng-app="app" route-class ng-class="{{routeClass.current}}">

Dynamically specify the controller to use in an Angular directive

I am looking to specify the controller that a directive should use via an attribute on the element - i.e. dynamically:
HTML
<div data-mydirective data-ctrl="DynController"></div>
Angular
angular.module('app', [])
.controller('MainController', [function() { ... }])
.controller('DynController', [function() { ... }])
.directive('mydirective', [function() {
return {
controller: 'DynController', // <- make this dynamic
...
}
}]);
You can do the following:
.directive('mydirective', [function() {
return {
controller: function($scope, $element, $attrs){
//Make decision based on attributes or $scope members
if($scope.$attrs.caseA == 'true'){
return new ControllerA($scope, $element, $attrs);
} else {
return new ControllerDefault($scope, $element, $attrs);
}
},
...
}
}]);
Taking it a step farther, you can use $controller
.directive('mydirective', ['$controller',function($controller) {
return {
controller: function($scope, $element, $attrs){
return $controller($attrs.dynamicCtrlName, {$scope: $scope,$element:$element,$attrs:$attrs});
},
...
}
}
In both cases, make sure you provide all the specific injectable dependencies (particular $scope, $attrs, transclude function etc...) your controllers expect. More details on controller injectables here $compile under the controller section - ideally, outer controller function should receive them all pass as locals.
Nothing in the docs about this, but you can do the following. From my understanding this stuff is used under the hood to make ng-controller work.
.directive('mydirective', [function() {
return {
controller: '#',
name: 'ctrl', // <- attribute that specifies the controller to use
...
}
}]);
I think a better approach is just use scope since scope is already bound to controller, i.e.
put what you want to call on controller scope and simple handle it in directive link

Categories

Resources