I'm new to Angular.js which is why I have a basic question regarding routing. I figured out how to create routes and inject specific .htmls by $routeProvider
var app = angular.module('test', ['ngRoute']);
app.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'routes/view2.html'
});
});
but what I really don't get is how content or function of view2.html are handled in Angular.
Lets take view2.html. It has a <p> with some text in a specific color. Nothing to special. But also it has a little slideshow which is called by $('slideshow').cycle() function.
All what happens is it displays me the <p> tag in a different color and no slideshow function is called on my rootsite of the app.
Could you give me some approach how to actually solve this?
Thanks
Just load required view and then compile it. During compilation Angular processes all directives in view.
If you want to do this proper(Angular) way you should put such code like $('slideshow').cycle() inside of directive. And then use it like
<div my-slideshow=""></div>
angular.module('myModule', [])
.directive('mySlideshow', [function () {
return {
restrict: 'A',
link: function (scope, element) {
element.cycle();
}
}
}]);
Directives documentation
Much more comprehensive documentation
Related
Here's one thing I'm used to do with angular directives
angular.module('app.directives').directive('login', ['$templateCache', function ($templateCache) {
return {
restrict: 'E',
template: $templateCache.get('directives/login/login.html'),
controller: 'LoginController as vm',
scope: true
};
}]);
I've grown very attached to using Template Cache to inject HTML content in my directive's template. Now with Angular 1.5 there's this new thing all the cool kids are using called component() which I'm giving a look to see if it's really good and I'm stuck at this very beginning part: how to inject things in the component itself (not in the controller)?
In this case you can see that I'm injecting into the login directive the $templateCache dependency. How would I rewrite this directive as a component? (keeping in mind my desire to use $templateCache over templateUrl)
Well, In Angular 1.5 components, template property can be a function and this function is injectable (documentation).
So, you can just use something like:
...
template: ['$templateCache', function ($templateCache) {
return $templateCache.get('directives/login/login.html')
}]
...
Few links from google search: one and two.
Hope it will help.
MaKCblMKo 's answer is right, but remember that AngularJS will check the templateCache first before going out to to retrieve the template. Therefore, you don't have to make this call in your directive or component.
angular.module('myApp', [])
.component('myComponent',{
templateUrl: 'yourGulpPattern'
})
.run(function($templateCache) {
$templateCache.put('yourGulpPattern', 'This is the content of the template');
});
https://jsfiddle.net/osbnoebe/6/
thanks for looking. I'll dive right in:
A JSON object has HTML links with ng-click attributes, employing ng-bind-html, with $sce's trustAsHtml to make the HTML safe. I have also used a custom angular-compile directive to compile the angular click listener into the app after the json is loaded in a $q promise.
All of this works as intended, at first glance...
JSON
{
"text" : "Sample of text with <a data-ng-click=\"__services._animation.openModal('g');\">modal trigger</a>?"
}
VIEW
<p data-ng-bind-html="__services.trustAsHTML(__data.steps[step.text])"
data-angular-compile></p>
DIRECTIVE
angular.module('app.directives.AngularCompile', [], ["$compileProvider", function($compileProvider) {
$compileProvider.directive('angularCompile', ["$compile", function($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.angularCompile);
},
function(value) {
element.html(value);
$compile(element.contents())(scope);
}
);
};
}])
}]);
The Issue:
Okay, so it works. My app loads, it serves the safe HTML, and my ng-click opens the modal, passing it's params. I see class='ng-binding' on the surrounding p tag, class="ng-scope" on the a tag in the generated html. Hooray!
The next order of business is to write that data in a another model that tracks progress, and run it through the same ng-bind, trustAsHTML, angular-compile treatment in another view. Just copying the data into a sibling object.
Here's where it fails!
<p data-ng-bind-html="__services.trustAsHTML(__state.modal.text)"
data-angular-compile></p>
In the second view, which is a modal in the same scope ($rootScope) on the same page - the bind, and trustAsHTML Angular is applied. But the link is not clickable, and no class="ng-scope" is generated on the a tag.
If fFurther explanation of my set-up might help understand the issue, let me detail it here. All initial app is set up by the concierge and it store most data in $rootScope:
return angular
.module('app', [
'ngResource',
'ngSanitize',
'ui.router',
'oslerApp.controllers.GuideController',
'oslerApp.services.ConciergeService',
'oslerApp.services.DataService',
'oslerApp.services.LocationService',
'oslerApp.services.StatesService',
'oslerApp.directives.AngularCompile',
])
.config(function($stateProvider, $urlRouterProvider) {
// For any unmatched url, redirect to landing
$urlRouterProvider.otherwise("/");
// Now set up the states
$stateProvider
.state('guide', {
url: "/guide",
templateUrl: "views/guide.html",
controller: 'GuideController as guide'
})
.state('contact', {
url: "/contact",
templateUrl: "views/contact.html"
})
})
.run(function(ConciergeService) {
ConciergeService.init();
});
I've spent 2 days refactoring my whole site to see if it was because the modal was in it's own directive, but putting it within the same template and scope didn't seem to help me here.
Lesson: If you have to refactor everything and still make no strides, you're doing it wrong.
To solve this, 2 days of hair pulling later, I made a tiny little service and pass the problem text through that:
compiler: function(element, template, content, scope) {
element = $(element);
template = template[0] + content + template[1];
var linkFn = $compile(template);
content = linkFn(scope);
element.replaceWith(content);
},
Here is my controllers.js file
(function(ctx,angular){
'use strict';
angular.module('app.controllers')
.controller('SearchMasterController',['$scope',function($scope){
//My Code
}]);
})(window, angular);
And this is my directives.js file
(function(ctx,angular){
function ControllerFunction(){
//My Controller Code
}
var directiveConfig = {
restrict:'E',
templateUrl:'path/to/acco.html',
controller: ControllerFunction
}
angular.module('app.directives')
.directive('acco', function(){
return directiveConfig;
});
})(window, angular);
Now my question is, can I use this acco directive with some different controller. Ideally, is there any way to get it working like
<acco ng-controller="SearchMasterController"></acco> ?
I tried doing,
<acco>
<div ng-controller="SearchMasterController"></div>
</acco>
and it seems to work.
Is it possible to use
<acco ng-controller="SearchMasterController"></acco> ?
The latter alternative looks ugly to me.
nice to heard this type of access, i have tried
<acco>hi{{name1}}
<div ng-controller="SearchMasterController">{{name1}}</div>
</acco>
<acco ng-controller="SearchMasterController">{{name1}}</acco>
<script>
angular.module('myApp', [])
.controller('SearchMasterController', ['$scope', function ($scope) {
//My Code
console.log("search");
$scope.name1 = 'james';
}])
.directive('acco', function () {
return{
restrict: 'E',
templateUrl: 'acco.html',
controller: function($scope) {
//My Controller Code
console.log("cntrlr fn");
$scope.name1 = 'semaj';
}
};
});
</script>
#that time i getting output as
cntrlr fn
search
cntrlr fn
means if we are using like
<acco>hi{{name1}}
<div ng-controller="SearchMasterController">{{name1}}</div>
</acco>
then we can access both controllers but when we are using like
<acco ng-controller="SearchMasterController">{{name1}}</acco>
we can't access SearchMasterController and it's not loaded also..
Yes you can use some different controller for your directive, but there is some best practice
Use controller when you want to expose an API to other directives. Otherwise use link.
The way you tried to use controller doesn't make much sense
<!--here acco and ng-controller both are directives,
in your directive's 'ControllerFunction' and ng-controller's 'SearchMasterController'
has the same controll (scope) for 'acco' rendered html.
In that case your directive's controller overrite ng-controller functionality.
So leave 'ng-controller',
if you need any functionality in your directive
then pass those functionality using =,&,#-->
<acco ng-controller="SearchMasterController"></acco>
I'm trying to use ng-view inside a custom directive but it's not working and it's also not giving me any console error.
This is my directive:
(function() {
'use strict';
angular
.module('app')
.directive('header', Header);
Header.$inject = ['USER_AGENT'];
function Header(USER_AGENT) {
return {
restrict: 'A',
templateUrl: 'app/shared/header/header.html',
controller: controller
}
function controller($scope) {
$scope.isMobile = USER_AGENT.isMobile;
}
}
})();
And inside header.html file I have a call to ng-view just like I was calling it outside (when it was working).
Is it possible to nest ngView inside a custom directive?
AngularJS does not support multiple ng-views in one app. If you want it - you have to use another routing engine, for example Angular UI's ui-router
Even if you could use it you shouldn't because is not the correct approach for Angular a directive should be treated like a web component like input, select, etc.
Just remember that your header.html is just a view and can be used by pretty much anything, is just the view
.directive('myDirective', function($timeout) {
return {
restrict: 'A',
templateUrl: 'app/shared/header/header.html',
controller: controller
});
Or (using ui-router)
$stateProvider
.state('home', {
url: '/?accountkey',
templateUrl: 'app/shared/header/header.html',
controller: controller
});
I dont know if this is a good practice... I have a controller defined in route config but because my HomeCtrl is in ng-if statement he cannot listen for loginSuccess so I made MainCtrl which listens for loginSuccess and reacts appropriately. This code works just fine but this smells like a hack to me. Should I remove MainCtrl and make it a service? If so some example would be really great.
Index.html
<body ng-app="myApp" ng-controller="MainCtrl">
<div ng-if="!isLoged()">
<signIn></signIn>
</div>
<div ng-if="isLoged()">
<div class="header">
<div class="nav">
<ul>
<li class="book">navItem</li>
</ul>
</div>
</div>
<div class="container" ng-view=""></div>
</div>
</body>
App.js
angular.module('myApp', [])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'HomeCtrl'
})
.otherwise({
redirectTo: '/'
});
})
.controller('MainCtrl', function ($scope) {
$scope.user = false;
$scope.isLoged = function(){
if($scope.user){
return true;
}else{
return false;
}
}
$scope.$on('event:loginSuccess', function(ev, user) {
$scope.user = user;
$scope.$apply();
});
})
.controller('HomeCtrl', function ($scope, $location) {
//this is home controller
})
.directive('signIn', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
//go to the server and then call signinCallback();
}
};
})
.run(['$window','$rootScope','$log',function($window, $rootScope){
$window.signinCallback = function (res) {
if(res){
$rootScope.$broadcast('event:loginSuccess', res);
}
else{
$rootScope.$broadcast('loginFailure',res);
}
};
}]);
I start all of my Angular projects with:
<html ng-app="appName" ng-controller="appNameCtrl">
The use of a "global" controller may not be necessary, but it is always nice to have it around when a need arises. For example, I use it in my CMS to set a binding that initiates the loading of everything else - so all the sub controllers are loaded because of it. That isn't violating separation of concerns because the global controller's concern IS to facilitate the loading of other controllers.
That said, just be sure to keep things as modular/separated and reusable as possible. If your controllers rely on the global controller's existence in order to function, then there is an issue.
In my opinion angular js' power comes with separating out clearly the different controllers directives, services, resources etc. Ideally controllers are linked to templates or partials and are used to update the front end and make calls to services or resources. The sooner you start making these separations the sooner you will start making clean and scalable apps that other developers can quickly make sense of. For app structure I would highly recommend you look into either of these two tools:
Lineman.js
and
Yeomann
The lineman site actually has a really good round up of how the two differ, if you scroll down.
In your scenario there are many ways to link controllers or make function calls that are in different scopes. You can either create a service that injects to your controllers or you can use $emit and $on to set up notifications in the app eg:
In controller A
$rootScope.$on('myNotifier:call', function() {
myFunction();
});
And in Controller B or any other controller you could call myFunction() with:
$scope.$emit('newPatientModal:close');