I am trying to use an ng-repeat that includes an ng-include. The problem is that the first element in the ng-repeat is just the ng-include template with none of the data from the ng-repeat filled in. Is there a way I can somehow bind the template from the ng-include so it works on the first ng-repeat?
<div ng-repeat="item in items">
<div ng-include src="'views/template.html'"></div>
</div>
For example, if my ng-repeat contains 10 items, then the first item that is rendered will just be the empty template. Items 2-10 WILL be rendered as they should be. What am I doing wrong?
First make sure that the data that is contained in the first index of items actually has the data that you want.
One possible solution to your problem would be to simply not show the first index of the ng-repeat:
<div ng-repeat="item in items" ng-show="!$first">
<div ng-include src="'views/template.html'"></div>
</div>
This may not actually tackle the root of your problem, but it may still get your application working a bit more like what you expect.
Another possible solution:
<div ng-repeat="item in items" ng-include="'views/template.html'"></div>
see example here:
http://plnkr.co/edit/Yvd73HiFS8dXvpvpEeFu?p=preview
One more possible fix just for good measure:
Use a component:
html:
<div ng-repeat="item in items">
<my-include></my-include>
</div>
js:
angular.module("app").directive("myInclude", function() {
return {
restrict: "E",
templateUrl: "/views/template.html"
}
})
I ran into the same problem, and finally figured out that the first element has not been fetched and compiled in time for the first ng-repeat iteration. Using $templateCache will fix the problem.
You can cache your template in a script tag:
<script type="text/ng-template" id="templateId.html">
<p>This is the content of the template</p>
</script>
Or in your app's run function:
angular.module("app").run(function($http, $templateCache) {
$http.get("/views/template.html", { cache: $templateCache });
});
You can also use $templateCache inside your directive, although it's a bit harder to setup. If your templates are dynamic, I would recommend creating a template cache service. This SO question has some good examples of template caching inside a directive and a service:
Using $http and $templateCache from within a directive doesn't return results
Using a directive worked for me: https://stackoverflow.com/a/24673257/188926
In your case:
1) define a directive:
angular.module('myApp')
.directive('mytemplate', function() {
return {
templateUrl: 'views/template.html'
};
});
2) use your new directive:
<mytemplate />
... or if you're concerned about HTML validation:
<div mytemplate></div>
Related
I am using bootbox, I need display product information in it. The product information is returned as json with rest call. I am thinking using a template, and transform from the json to html. I need ng-repeat etc, in the template. The idea way is I can call template and get a html result.
But it seems angularjs $compile need bind to element to render. any idea?
I think you can use ng-include:
var app = angular.module('myApp', []);
app.controller('productCtrl', function($scope) {
$scope.productInfos = [];
});
Use ng-include (You have to the adjust the path depending the location of your template)
<div ng-app="myApp" ng-controller="productCtrl">
<div ng-include="'product-information.html'"></div>
</div>
You can do ng-repeat in product-information.html:
<div ng-repeat= "info in productInfos"> {{ info.prop1 }}</div>
Use case:
In my index.html. I have two ng-include directives and two custom directives.
Each directive has its src attribute defined the address to load some html snippet from another server (the custom directives have its template attribute loading the html)
I need to make sure that all directives (see the code 1,2,3) have fully loaded the html snippets before I call the fifth ng-include directive (4) to load the java-script from another server.
Right now all directives are running asynchronously and I can't be sure that the directive which loads the Javascript should always be the last one to load. I need this because if Javascript is loaded and executed before the html it wouldn't go through the html and linking it to javascript/jQuery.
I have solved this issue using a very classic way. I update a global variable whenever the html snipts are loaded and define the an ng-include directive to load the Javascript conditionally when the global variable is true indicating all other directives have loaded their html. But this is not the Angular way. I need the Angular way and btw I'm new starter on Angular.
Index.html
<div ng-controller="navigationController" >
**(1)**<agilesites-navbar></agilesites-navbar>
<div class="container-fluid">
<div class="row row-offcanvas row-offcanvas-right">
<div id="offcanvas-curtain" class="hidden-lg"></div>
<div id="contentContainer" class="col-sm-12 col-md-12 col-lg-12">
<div ui-view="content" id="content"></div>
</div>
**(2)**<agilesites-sidebar></agilesites-sidebar>
</div>
</div>
**(3)**<ng-include src="''+API_HOST+'api/cms/footer'" onload="cmsResourceLoadingStatus(true)"></ng-include>
**(4)**<ng-include src="''+API_HOST+'api/cms/javascript'" ng-if="cmsResourceLoadingCompleted"></ng-include>
</div>
Custom directive (agilesites-sidebar):
angular.module('newhorizonsApp')
.directive('agilesitesSidebar', ['ProductTypes','$compile', function (ProductTypes,$compile) {
return {
restrict: 'E',
scope: true,
template: '<ng-include src="\'\'+API_HOST+\'api/cms/sidebar\'" onload="executeOnLoad();"></ng-include>',
link: function (scope,elem){
scope.executeOnLoad = function() {
scope.cmsResourceLoadingStatus(true);
};
}
};
}]);
I'm still fighting with simple things in Angular. I have jQuery and Backbonejs background, so please do not yell on me. I try hard to understand differences
I have HTML in which from rails is given ID of project as data-project-id:
<div data-ng-controller="ProjectCtrl as ctrl" data-project-id="1" id="project_configuration">
Is there any chance to get access to this attribute? I need it to my API calls...
To access an elements attributes from a controller, inject $attrs into your controller function:
HTML
<div test="hello world" ng-controller="ctrl">
</div>
Script
app.controller('ctrl', function($attrs) {
alert($attrs.test); // alerts 'hello world'
});
In your example, if you want to get data-project-id:
$attrs.projectId
Or if you want to get id:
$attrs.id
Demo:
var app = angular.module('app',[]);
app.controller('ctrl', function($attrs) {
alert($attrs.test);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl" test="hello world">
</div>
In Angular world you should use directives to manipulate with DOM elements. Here a nice explanation how to get attribute value from custom directive (How to get evaluated attributes inside a custom directive).
But if you still want to get it's value from controller you are able to use jQuery as well $('#project_configuration').data('project-id')
Or perhaps a better question is, should a directive contain a controller?
For reasons of separation, my index.html is a simple file. Everything is rendered into it via templates. So my index.html is real simple:
<body ng-app="myapp"><mainmenu></mainmenu><div ng-view></div></body>
I don't really need a directive for mainmenu, but it allows me to put the menu in a separate template file. The main menu itself contains user info, login/logout, and a search box.
<div class="leftmenu" ng-show="isLogin()">
<ul class="menu">
<li>Part1</li>
<li>Part2</li>
<li>Part3</li>
</ul>
<div ng-controller="Search" class="Search><input type="text" ui-select2="s2opts" style="width:250px;" ng-model="search" data-placeholder="search"></input></div>
</div>
<div class="rightmenu">
<ul ng-show="isLogin()" class="menu">
<li>My Account</li>
<li>Logout</li>
</ul>
<ul ng-show="!isLogin()" class="menu">
<li>Login</li>
<li>Register</li>
</ul>
</div>
So there is the menu part, with its own controller, and the search, with its own, embedded between the two parts.
Of course, my mainmenu directive unit tests fail because SearchController isn't defined. But this leaves me wondering if I am going about this wrong. Should I even have it like this, a section of html with an explicit ng-controller defined inside it? Doesn't this create all sorts of weird dependencies?
How should I better structure this? A search directive that is included so I can unit test it separately? Something feels wrong here structurally...
UPDATE:
As requested, a basic fiddle: http://jsfiddle.net/nj4n44zx/1/
As specified by the Angular documentation, the best practice is to define a controller inside a directive only to expose an API to another directive. Otherwise the link function is sufficient.
See at the bottom of :
Angular directives
By experience using controllers inside a directives shadow what you are doing in your scope. It does not help to have a easy readable code.
I do prefer using the main controller where the directive is included. With a non isolated scope you have access to everything from the link function.
I usually deal with it like that:
app.directive('topMenu', function() {
return {
restrict: 'E', // or whatever You need
templateUrl: '/partials/topmenu', //url to Your template file
controller: function($scope) {
$scope.foo = "bar";
}
};
});
Then, in that template You don't have to add ng-controller.
sure your directive can contain a controller because you declare a directive like this
myApp.directive('mainMenu', function() {
return {
restrict: 'E',
replace: true,
scope: true,
templateUrl: 'menu.html',
controller:['$scope', function($scope) {
//define your controller here
}]
};
});
I need to use angular and masonry and also implement sorting and filtering.
The fiddle here does use the masonry with Angular and has filtering and sorting working, however the layout does not seem like masonry. I do not think the masonry layout is applied at all.
http://jsfiddle.net/rdikshit/6swek/3/
<div ng-app="test">
<div ng-controller="MainCtrl">
<input type="text" ng-model="nameFilter" />
Order by id
Order by name
Order by age
<div class="items" masonry >
<div ng-repeat="item in items | filter: { name: nameFilter } | orderBy: order:reverse" class={{item.style}}>
<span>{{item.name}}</span>
<span>id: {{item.id}}</span>
<br /> <span>Age: {{item.age}}</span>
<br /> <span>Style: {{item.style}}</span>
</div>
</div>
</div>
</div>
Here is another fiddle with Passy's directive:
http://jsfiddle.net/rdikshit/6swek/5/
STill does nt work. Even the sorting and filtering are not working now.
I modified your JSFiddle and got it to work. Since columnWidth is not specified, the width of the first element is used. That's why there is gap between the elements sometimes
Few points to look out for:
Don't save the Masonry instance, since you are using the JQuery version. To access Masonry methods, simply get the container element then do container.masonry( 'methodName', arguments );
The watches in the controller are not ideal, in a real app you probably want to put them into a directive
The technique where you watch the number of children in the Masonry container doesn't work when you have ngAnimate as one of your dependency. It cause the DOM tree to be updated after $digest(), which make $watch miss
I am working on a simular project, but i am using Isotope with a mansory style.
here is the way i am using it:
Directive:
app.directive('isotope', function ($timeout) {
return {
scope: {
items: '=isotope'
},
templateUrl: 'social-item.html',
link: function (scope, element, attrs) {
var options = {
animationEngine : 'jquery',
itemSelector: '.social-item',
layoutMode: 'masonry',
masonry : {
"gutter": 10,
"isFitWidth": true
}
};
element.isotope(options);
scope.$watch('items', function() {
$timeout(function() {
element.isotope( 'reloadItems' ).isotope();
});
},true);
}
};
});
Directive template (social-item.html):
<div class="social-item" ng-repeat="item in items track by $index">
<!--
Do something with item, in your case something like this:
<span>{{item.name}}</span>
<span>id: {{item.id}}</span>
-->
</div>
HTML Markup:
<div class="social-wall-container" isotope="socials.posts">
<!-- repeat posts from directive -->
</div>
For more information about isotope checkout This link
If you decide to use isotope, make sure to include the necesary files located in the JS folder on the github page.