I need to create a base view, and include "modules" in it.
What I call module is : a View (HTML page), a Controller (Javascript) and a Style (CSS).
At the moment, I need to includes all my javascripts at the end of the index.html and my css at the beggining... BUT, sometimes I won't load ALL my modules.
So if in my page, I need to display module 1 / 2 / 3, I only want to include their views / controllers / styles, and not Module 4's.
I tried with ngView and ngInclude, but I always get an error when I put the related javascript.
Here is an example of what I would like :
<div ng-include="'modules/module1.html'"></div>
The result would be ===>
<link href="modules/module1.css" />
<div ng-controller="module1Controller as module1Ctrl">
<h3 class="test">Module 1</h3>
<p>It is a test !</p>
</div>
<script src="modules/module1.js"></script>
I hope it makes sense...
Your directive -
app.directive('module1', function () {
var controller = ['$scope', function ($scope) {
//CONTROLLER FUNCTIONS HERE
}];
return {
restrict: 'E',
scope: {
data: '=data'
},
controller: controller,
templateUrl: 'module1.html'
};
});
module1.html:
<h3 class="test">Module 1</h3>
<p>It is a test !</p>
Then on your view
<module1 data="THIS COULD BE AN OBJECT" />
In regards to your css you should really be using something like LESS or SASS, creating separate files, then building one big global CSS file using some sort of task runner like Grunt, Gulp.
All your CSS pertaining to module1 can just start with module1
Example:
module1 h1{font-size:24px;}
module1 div.body{width:100px;}
Here is a link to the plunk... https://plnkr.co/edit/epD6ckxaXxbGHssGaJhu?p=preview
Related
I have the following code. I expected the compile to run only once, and link to run 5 times.
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js" ></script>
<style>.red{color:red;}.blue{background:blue;}</style>
</head>
<body ng-app="myApp" ng-controller="myCtrl">
<div hello dear> 1 </div>
<div hello dear> 2 </div>
<div hello dear> 3 </div>
<div hello dear> 4 </div>
<div hello dear> 5 </div>
<script>
//module declaration
var app = angular.module("myApp",[]);
//controller declaration
app.controller('myCtrl',function($scope){
//no code
});
//directives declaration
app.directive('hello',function(){
return{
restrict: 'A',
compile: function(tElement, tAttributes){
tElement.addClass("red");
console.log("compiled");
},
}
});
app.directive('dear',function(){
return{
restrict: 'A',
link: function($scope, element, attributes){
element.addClass("blue");
console.log("linked");
}
}
});
</script>
</body>
</html>
Expectation:
Compile to run once. Link to run 5 times.
Result:
Both compile and Link running 5 times.
Screen-shot:
Reference:
http://www.bennadel.com/blog/2794-when-do-you-need-to-compile-a-directive-in-angularjs.htm
Can someone tell me why compile running 5 times (OR, how to run it only once)?
The compile phase comes before the link phase for every directive, since you applied the hello directive on 5 elements it's expected behaviour to compile and then link 5 times. A more detailed explanation of the linking process of a directive with a subdirective can be found here.
Compile function
Each directive's compile function is only called once, when Angular bootstraps.
Officially, this is the place to perform (source) template manipulations that do not involve scope or data binding.
Primarily, this is done for optimisation purposes; consider the following markup:
<tr ng-repeat="raw in raws">
<my-raw></my-raw>
</tr>
The <my-raw> directive will render a particular set of DOM markup. So we can either:
Allow ng-repeat to duplicate the source template (<my-raw>), and then modify the markup of each instance template (outside the compile function).
Modify the source template to involve the desired markup (in the compile function), and then allow ng-repeat to duplicate it.
If there are 1000 items in the raws collection, the latter option may be faster than the former one.
Do:
Manipulate markup so it serves as a template to instances (clones).
Do not:
Attach event handlers.
Inspect child elements.
Set up observations on attributes.
Set up watches on the scope.
That's the intended behavior though.
Have a look at https://docs.angularjs.org/api/ng/service/$compile
The whole flow (compile, controller, pre/post-link) happens for each directive tag.
This would make your compile run only once:
<hello>
<div dear> 1 </div>
<div dear> 2 </div>
<div dear> 3 </div>
<div dear> 4 </div>
<div dear> 5 </div>
</hello>
Though in that case, there is no need to use the compile phase, just use the link
app.directive('hello',function(){
return{
restrict: 'AE',
link: function(scope, iElement){
iElement.children().addClass("red");
console.log("\"compiled\"");
},
}
});
Edit: if you need hello directive's code to run before dear's, put the code in prelink instead of link.
.directive('hello', function() {
return {
restrict: 'AE',
compile: function() {
return {
pre: function(scope, iElement) {
iElement.children().addClass("red");
console.log("\"compiled\"");
}
}
},
}
})
I have a LAMP website that i am trying to convert to be built on MEAN and I've split the index page into partials so i could have a SPA.
I am completely new to MEAN but i'm slowly learning and trying things however i'm now stuck on JQuery slider plugin that I am using in one of my partials and it does not load any images in the placeholder when the website is ran.
I know that i am supposed to use directives so that JQuery function is loaded after the partial page is loaded into the main page and hence why nothing is being displayed where the slider should be.
This is my partial page that contains the snippet of the slider for a single image (there are 3 of these 'li' blocks at the moment for 3 images):
home.html
<div id="pm-slider" class="pm-slider">
<ul class="pm-slides-container" id="pm_slides_container">
<li data-thumb="public/app/img/home/slide1a.jpg" class="pmslide_0"><img src="public/app/img/home/slide1.jpg" alt="img01" />
<div class="pm-holder">
<div class="pm-caption">
<h1>Medical professionals</h1>
<span class="pm-caption-decription">
that you can trust
</span>
learn more <i class="fa fa-plus"></i>
</div>
</div>
</li>
</ul>
</div>
My index.html file contains div ng-view tag where all of home.html should go.
app.js contains this code for routing and I know that here should go the .directives function for the JQuery plugin
var digiProm = angular.module('digiProm', [
'ngRoute']);
digiProm.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/', {
templateUrl: 'public/app/partials/home.html'
})
.when('/services', {
templateUrl: 'public/app/partials/services.html'
})
.otherwise({
redirectTo: 'partials/home.html'
});
}]);
After watching multiple videos as well as tutorials i can't figure out how i should write my directive function to work.. I am trying to work out how to use this type of format
digiProm.directive('slider', ['$rootScope', function($rootScope) {
return {
restrict: 'EA',
templateUrl: '/path/to/template',
link: function(scope, iElement, attrs) {
//attrs references any attributes on the directive element in html
//iElement is the actual DOM element of the directive,
//so you can bind to it with jQuery
$(iElement).bxSlider({
mode: 'fade',
captions: true
});
}
};
}]);
Is the templateUrl here supposed to be the path to the jquery file for the slideshow?
What the heck would go inside the link: function(...) ?? This script has been shown as an example on how to use the bxSlider and i'm trying to use a Pulse PM-Slider so not sure what functions i'm supposed to call for it to load after the partial has been loaded...
Any kind of help is much appreciated.
Thank you in advance
If the directive is only activating on an element that already exists in another template then you don't need templateUrl.
That is used to be able to inject html into an element. in other words a directive can define it's own template
Also the restrict is to tell directive whether to look for E-an element , or A an attribute or C- a class.
I suggest you add an attribute since it makes it easier to see later on in the markup
<div slider id="pm-slider" class="pm-slider">
Then remove templateUrl from your directive declaration object.
Also note that when jQuery is included in page before angular.js iElement is already a jQuery object so you can just use iElement.bxslider({....})
I am trying to use the angular-seed project (https://github.com/angular/angular-seed) to start a new project. I am trying to add in a new directive.
Created testDirective.js file :
'use strict';
angular.module("myApp.testDirective", [])
.directive("testDirective",
function() {
return {
template : "<h1>Made by a directive!</h1>"
};
});
Included the file in index.html, added "myApp.testDirective" into app.js, in view1.js I added :
'use strict';
angular.module('myApp.view1', ['ngRoute','myApp.testDirective'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/view1', {
templateUrl: 'view1/view1.html',
controller: 'View1Ctrl'
});
}])
.controller('View1Ctrl', [function() {
}]);
and view1.html looks like :
<p>This is the partial for view 1.</p>
<div testDirective></div>
However, when I run the app, nothing shows up where testDirective is. Not sure where I've gone wrong.
Whenever you wanted to use directive on the html, you should just replace capital letter to small case prefix - with it. Directive normalization process convert that test-directive to testDirective while compiling directive.
<div test-directive></div>
When including attribute directives inside the opening tag of an HTML element, they must be converted to dashes.
<div test-directive>
In my AngularJs Project I have a DIV tag which contains the main navigation bar in my project.
This navigation bar has it's own specific controller which called "topNavController".
I've recently modified my app in order to load everything -somehow- lazily using RequireJs .
The problem is this controller is not dependant on any view/route and has to be loaded directly after my App.js loads .
I'm bootstrapping my app from my RequireJs main file using :
angular.bootstrap(document, ['myApp']);
If I do like :
<div ng-controller="topNavController">...</div>
I get an error which says :
Argument 'topNavController' is not a function, got undefined
which I think it's because myApp is not bootstrapped at the moment Angular tries to bind this controller to my DIV.
I appreciate any suggestions, workarounds or solutions for this.
Thanks in advance.
You miss ng-app to launch the angular bootstrap method.
<div ng-app="myApp">
<div ng-controller="topNavController"></div>
</div>
You should use directive for this behavior
I've fixed it by removing
ng-controller='topNavController'
and making a directive for it, I put all the code from topNavController to topNavDirective and used directive's controller instead.
Thanks to "iScor" hint in the answer above !
here is how the last version of my main RequireJs file looks like :
require(
[
'app',
'routeResolver',
'services/authService',
],
function () {
require(["directives/topNavDirective"]);
angular.bootstrap(document, ['myApp']);
});
I worked through the tutorial on the AngularJS website and I noticed that in at step 7, they change how a controller is introduced into the application. Initially, they use a directive:
<body ng-controller="PhoneListCtrl">
...
</body>
However, it later gets changed to use a controller attribute as part of an ng-route.
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
/* rest of routes here */
Here's the git diff where the change is made. Is there a difference between these two techniques?
Controller using a ng-controller directive:
A new $scope is created on ng-controller element.
Explicit view-to-controller connection
Visible with inspect element, etc
Controller in a route:
A new $scope is created per route on the ng-view element.
The controller can request dependencies defined in the route resolve.
Optional view-to-controller connection. Recommended to have a naming convention that maps routes to controllers to views.
One of well-known feature of Angularjs is Single-Page Applications.
If you assign ng-controller attribute directly on the page:
<body ng-controller="PhoneListCtrl">
...
</body>
you can't switch controllers easily for other tasks.
So, use route to switch controllers is one of important step in learning Angular Single-Page feature.
You can have same layout and one different element by using route and ng-view directive.
$routeProvider.
when('/phones', {
templateUrl: 'partials/phone-list.html',
controller: 'PhoneListCtrl'
}).
when('/tablets', {
templateUrl: 'partials/tablet-list.html',
controller: 'TabletListCtrl'
}).
If '/phones'
<div ng-view></div>
will include your 'partials/phone-list.html' template
and set 'PhoneListCtrl' as div controller
The same:
If '/tablets'
<div ng-view></div>
will include your 'partials/tablet-list.html' template
and set 'TabletListCtrl' as div controller
This is the difference between two.
ng-view is the cause of the difference. You can't really do this
<div ng-view ng-controller="PhoneListCtrl">
As you'd need to change that controller as the route changed. So basically the router does that for you, and uses the controller you specified when you defined your routes.
You probably can do this:
<div ng-view>
and then in your template:
<div ng-controller="PhoneListCtrl">
and leave out the controller declaration in your routes. Which I suspect would have essentially the same effect, although I've never tried that. Probably better to go with convention here though.
In the 1st case the controller is directly on the page.
Once they change it, that controller is only on the page if the route is /phones otherwise it is some other controller based on some other route.
Yes - the change is this:
if you want to display a specific controller on the page, you can use
<body ng-controller>
BUT
if you want to do routing (application with more than one controller) - you will need to use routing + change the body to:
<body ng-view></body>