So, I have the following situation: in my main index.html page:
<aside>
<ul class="nav nav-pills nav-stack" id="myAffix">
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
</aside>
and in my menus.js:
$('#myAffix').affix({
offset: {
top: 100
}
});
This is supposed to make the unordered list stick after scrolling down 100px. And it does the job perfectly.
But, after moving my aside to another .html file (aside.html), and including it in my index.html like this:
<div ng-include="'aside.html'"></div>
the aside element won't stick anymore. I am also loading the script at the end of the body tag in index.html. How can I fix this issue?
EDIT: Working plunker solution: http://plnkr.co/edit/rQJt3h
Yes you're having a load order issue. The solution involves using a directive.
In my opinion, people are being opinionated when telling not to use jQuery with AngularJS. Yes - you should do all DOM manipulation in directives.
In order to use the Bootstrap JavaScript Plugin, you can wrap the affix initialization in a directive:
app.directive('bootstrapAffix', function() {
return {
link: function(scope, element, attrs) {
$(element).affix({ offset: $(element).parent().offset().top });
}
}
});
And then attach it to your element:
<aside bootstrap-affix> ... </aside>
Remember to include CSS!
Related
I've got this accordion made using materializecss framework:
<ul class="collapsible" data-collapsible="accordion">
<li id="licollapse" ng-repeat="single in packageNames">
<div class="collapsible-header">{{single.name}}</div>
<div class="collapsible-body"><p>{{single.name}}</p></div>
</li>
</ul>
and really i tried in several ways to init this accordion but without success! I tried to write in the html view:
<script>$('.collapsible').collapsible();</script>
and it not worked, i tried create in my controller
$scope.collapsible = function() {
$('.collapsible').collapsible({
accordion : false // A setting that changes the collapsible behavior to expandable instead of the default accordion style
});
};
and call collapsible function in ng-init of the accordion and it not worked. The only one solution i found is call that function in ng-click in the collapsible-header but it's not best way because it works only at the second click.. How can i solve?
When you are forced against your dying wish to call external jQuery frameworks or use jQuery in your Angular Application, then the best approach would be to do so in a directive!!!
The DOM should never be touched in your controller, instead, the state of your data in the controller should update the view.
So in this case we can create a simple directive that will have access to that element and call the function in question:
app.directive('collapsify', [collapsifyFn]);
function collapsifyFn(){
return {
restrict: 'A',
compile: function(element, attrs) {
return {
pre: function preLinkFn(scope, element, attrs) {
//if executed here collapsable only is called on an empty <ul>
},
post: function postLinkFn(scope, element, attrs) {
function linkFn(scope, element, attributes) {
debugger;
$(element).collapsible({accordion: true});
}
}
}
}
}
}
An important thing to note here:
Use case for Pre and Post Link:
So because you are generating your <li>'s via an ng-repeat, you need to ensure that you are calling .collapsible in your link function after ng-repeat has rendered the <li>'s to the DOM.
Because of rendering priority, a simple link: would cause the ng-repeat link: to execute after, making your collapsable function not work properly. The purpose of post: is that it executes the linkFn after its children's linkFn has already executed.
Therefore you are accurately calling .collapsible on a "completely rendered dom".
Now that we have created our superfancy collapsify directive, we can attach it to the dom.
<ul collapsify class="collapsible" data-collapsible="accordion">
<li id="licollapse" ng-repeat="single in packageNames">
<div class="collapsible-header">{{single.name}}</div>
<div class="collapsible-body"><p>{{single.name}}</p></div>
</li>
</ul>
Example From Codepen:
<p data-height="268" data-theme-id="0" data-slug-hash="meoWEK" data-default-tab="result" data-user="TheLarkInn" class='codepen'>See the Pen <a href='http://codepen.io/TheLarkInn/pen/meoWEK/'>Using Collapsible MaterializeCSS</a> by Sean Larkin (<a href='http://codepen.io/TheLarkInn'>#TheLarkInn</a>) on <a href='http://codepen.io'>CodePen</a>.</p>
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>
You should wait with calling your materialize code like this:
$(document).ready(function(){
$('.collapsible').collapsible({
accordion : false
});
});
Do it in this way
<ul class="collapsible" data-collapsible="accordion">
<li id="licollapse" ng-repeat="single in packageNames">
<div class="collapsible-header">{{single.name}}</div>
<div class="collapsible-body"><p>{{single.name}}</p></div>
</li>
<script>
//Instert this just after closing </li>
$(document).ready(function() {
$('.collapsible').collapsible({
accordion: true
});
});
</script>
</ul>
It will work for sure :)
The code is written by a former front-end developer of my company. After he left, I was asked to edit this page. When I finished working on CSS, I found the affix which formerly worked, will always activate the last li element. I tried a lot of ways but have no idea why this is happening.
Example code:
<ul id="myNav" class="nav nav-tabs nav-stacked">
<li>关于Speed Dating</li>
<li>活动流程</li>
<li>成功案例</li>
<li>往期回顾</li>
<li>地点</li>
<li>合作伙伴</li>
</ul>
......
<h2 id="section-1">关于Speed Dating</h2>
<h2 id="section-2">关于Speed Dating</h2>
......
The whole code is on CodePen
Finally, I found it's because the former marked with attributes data-spy="scroll" data-target="#myScrollspy" was now on a div element.
I deleted the body tag because the former HTML has two body tag, the first one is set in the base template, and the second one is written in HTML to bind bootstrap affix. So bootstrap affix doesn't work after I change the second body tag to div.
TL,NR: It fixed after I add attributes data-spy="scroll" data-target="#myScrollspy" to body element.
I have the following directive in my project:
app.directive('eventSessionsList', function() {
return {
restrict: 'AEC',
scope: {
input: '=data'
},
templateUrl: 'directives/event-sessions-list.html'
};
});
The template looks like this:
<ul class="event-sessions-list">
<li ng-repeat="session in input.eventSessions">
<span class="date">{{ session.date }}</span>
<p class="info">
{{ session.length }} hr session # {{ session.venue }}</p>
</li>
</ul>
When I try to load the page it crashes with no errors (tested in both Safari and Chrome).
The mistake was a simple one, but to help you avoid it here's what I did wrong: The name of my CSS class on the UL element is the same as the name of my directive (angular equates hyphenated words and camel case). This means that angular interpreted the CSS class as a call to instance the directive. This created an infinite nesting loop.
To fix this problem I changed the name of the class from "event-sessions-list" to "sessions-list".
I hope this saves you tearing your hair out!
I had a very similar problem but instead of a clash with CSS classes (OP's case), I had it with HTML tags.
Just posting in case someone runs into this slightly different variation of the same root problem.
HTML
<div id="header">
<div id="header_main">
<nav></nav>
</div>
</div>
Nav Template
<div class="dark-blue-section main-color">
<div class="container">
<nav class="navbar" role="navigation">
Nav Component
angular
.module('common')
.component('nav', {
templateUrl: './nav.html',
});
Like OP said, simply renaming it will solve it.
NOTE: I did first try renaming from nav to navbar which is ALSO an HTML class if you look at the Nav Template HTML, but this did not seem to confuse AngularJS.
Not sure why CSS classes cause confusion, but HTML classes don't so maybe someone else can chime in there.
I'm making my first steps with angularJS. In the "code school" video (http://campus.codeschool.com/courses/shaping-up-with-angular-js/level/2/section/2/video/1) there is a sample code that makes tabs in angular:
HTML
<section class="tab" ng-controller="TabController as tab">
<ul class="nav nav-pills">
<li ng-class="{active:tab.isSet(1)}">
<a href ng-click="tab.setTab(1)">Description</a></li>
<li ng-class="{active:tab.isSet(2)}">
<a href ng-click="tab.setTab(2)">Specs</a></li>
<li ng-class="{active:tab.isSet(3)}">
<a href ng-click="tab.setTab(3)">Reviews</a></li>
</ul>
<div ng-show="tab.isSet(1)">
<h4>Description</h4>
<blockquote>{{product.description}}</blockquote>
</div>
<div ng-show="tab.isSet(2)">
<h4>Specs</h4>
<blockquote>Shine: {{product.shine}}</blockquote>
</div>
<div ng-show="tab.isSet(3)">
<h4>Reviews</h4>
<blockquote></blockquote>
</div>
</section>
JavaScript:
app.controller('TabController', function(){
this.tab = 1;
this.setTab = function(newValue){
this.tab = newValue;
};
this.isSet = function(tabName){
return this.tab === tabName;
};
});
I know that twitter bootstrap has its own JavaScript for managing dynamic tabs (http://getbootstrap.com/javascript/#tabs).
My question is: is angularjs using bootstrap javascript here? I guess not. And if not (while this means angular is using only bootstrap's CSS), then why is angular reinventing the wheel = implementing new code that does the same thing as bootstrap's javascript code? I mean, why writing different code that does the same stuff, why not to use existing code?
Maybe it's just a matter of this tutorial - but is there a way to make angular use native bootstrap's javascript?
bootstrap provides one-way binding data. Howerver, angularJS supplies two-way binding. I suggest you should take a look at angular-ui. If you want use bootstrap with angularjs, you shoud search keyword "custom directive angularjs".
Reinventing the wheel? They are only using ng-show, which just changes the display style in your element to none.
Angular has no problem to use anyone scripts, you only have to do it the angular way. In this case it is called directives (most cases when you are going to manipulate DOM this is the way).
So for angular directives you can use template or templateUrl, template you give a string, templateUrl you give a file path. In your case I recommend you to place a new html file and write there your tabs content.
so in you tabs.html file
<div class="tab-content">
<div class="tab-pane active" class="home">...</div>
<div class="tab-pane" class="profile">...</div>
<div class="tab-pane" class="messages">...</div>
<div class="tab-pane" class="settings">...</div>
</div>
your directive should look something like this, according to bootstrap docs
myapp.directive('theNameOfMyDirective', function () {
return {
restrict: 'E',
replace: true,
templateUrl: 'the path to my html file', you can use only template too and writte your html as string, but in your case I think it is more clean if you do it in a diferent file
link: function (scope,element){
// angular.element() this is similar to $() in Jquery or Jquery()
angular.element(element).find('.profile').on('click', function (e){
e.preventDefault()
$(this).tab('show');
});
angular.element(element).find('.home').on('click', function (e){
e.preventDefault()
$(this).tab('show');
});
//some other tabs
}
}
});
since we restrict our directive to be E (Element), we have to add this html to render our tabs wherever we need them
<theNameOfMyDirective></theNameOfMyDirective>
for more info of custom directives http://tutorials.jenkov.com/angularjs/custom-directives.html
I'm sucessful create and display templates with some data retrieved from REST service using AngularJS but, when JSON response is still loading, the browser show the footer template at the top and, when response return the JSON data, the footer goes to the bottom.
This occurs very quickly, but the footer template blinks at the top of the page before goes to the bottom.
I've tried using the ng-cloak approach, unfortunately, the problem still happening. I put the CSS to ng-cloak as the API Reference recommend.
Here is my app code:
<body>
<div data-ng-controller="HeaderCtrl" data-ng-include="'app/partials/header.html'"></div>
<div data-ng-controller="MenuCtrl" data-ng-include="'app/partials/lista-menu.html'"></div>
<div ng-view="main" ></div>
<footer class="nav" data-ng-include="'app/partials/footer.html'" ></footer>
I try put the ng-cloak on body tag, ng-view, footer, and also inside the ng-view html template. This code represents all attempts (Note: I've try to use separately and together, with ng-cloak class and not)
<body ng-cloak class="ng-cloak">
<div data-ng-controller="HeaderCtrl" data-ng-include="'app/partials/header.html'"></div>
<div data-ng-controller="MenuCtrl" data-ng-include="'app/partials/lista-menu.html'"></div>
<div ng-view="main" ng-cloak class="ng-cloak"></div>
<footer class="nav" data-ng-include="'app/partials/footer.html'" ng-cloak class="ng-cloak"></footer>
Unfortunately after all these changes, the footer template still blink on top before loading is complete.
Anyone can help me to fix this?
Is any Bootstrap trick to put the footer on bottom, even when the main div is without height? I've tried use the nav-fixed-bottom tag but I dont want to have the bottom fixed on screen when the page has high height values.
Thanks!!!
Have you double checked whether you have any CSS rules that may be conflicting with the ng-cloak rule? This could happen with other styles, libraries etc.
If you have any rules that conflict, just adding display:none; may not be enough.
See Angularjs - ng-cloak/ng-show elements blink
If this is the case, the solution is to use !important to overcome this:
[ng\:cloak], [ng-cloak], .ng-cloak {
display: none !important;
}
ng-cloak and/or ng-bind can't help solve this problem, because this is not a "flash of uncompiled content" problem. Those directives are meant to hide content until Angular has had a chance to compile the HTML.
The problem you are trying to solve is more like: "I'm adding stuff to the page dynamically and stuff is moving around". If you want to show the footer only after the main content is loaded, I like the solution #Alex presented in his comment.
As Alex and Mark said, ng-cloak doesn't provide any benefit in this case. However I used something that worked for me and may also help others.
Initially, I don't display the footer.
.footer {
display: none;
}
then after the Angular is done with loading the content, the footer appears.
var app = angular.module('app', [...])
.run(function($rootScope) {
$rootScope.$on('$viewContentLoaded', function(event){
$('.footer').fadeIn(500);
});
});