I have some legacy jQuery code. It's a big chunk of code, so I would prefer to port it a little while later. To use it, I call $('#legacyId').legacyFunction().
Here's the deal, though.
I have an ng-if. And within that ng-if, I have the JavaScript where I call my legacy function.
<div ng-if="status=='yes'">
<div id="legacyId">
I am a legacy div!
</div>
<script type="text/javascript">
$('#legacyId').legacyFunction()
</script>
</div>
It looks like this JavaScript is being called when the page loads. Though I load angular after jQuery, it seems that angular removes the section under control of the ng-if, and therefore the jQuery function on #legacyId fails.
Any ideas? Thanks!
Edit-note: I import jQuery and the legacy code that extends jQuery at the top of my HTML document. Angular is loaded at the bottom of the document.
Edit-note 2: I have also tried <div ng-init="$('#legacyId').legacyFunction()"></div>, but I get an error, Error: [$rootScope:inprog] $apply already in progress.
Okay, managed to work this out.
In the HTML:
<div ng-if="status=='yes'">
<div legacy-directive>
I am a legacy div!
</div>
</div>
In my app code:
app.directive('legacyDirective' , function() {
return {
link: function(scope, element, attrs) {
$(element).legacyFunction(scope.$eval(attrs.legacyDirective));
}
}
});
Because of the $(element).legacyFunction(scope.$eval(attrs.legacyDirective));, it looks like I can also pass parameters to the legacyFunction; e.g. <div legacy-directive='{myParameter: true}>
Thank you for all who answered! You set me on the right track.
Related
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);
};
}
};
}]);
there are many examples in SO on how we can use an angular directive to know when an image is loaded. The examples describe a directive that is directly applied to the img element like so:
<img ng-src="myimage.jpg" my-great-directive />
And then in the directive "my-great-directive"
we can do an:
element.bind("load" , function(e){ }
And i works great.
However, what if the construct is like this:
<div my-great-directive>
<img ng-src="myimage.jpg" />
</div>
Within my-great-directive, how do I bind to the inside image loaded event?
This is not really a theoretical problem for me. I am using a 3rd party slider called angular-carousel-slider that wraps around like so:
<ul rn-carousel rn-carousel-buffered
rn-carousel-index="mycarousel.index"
rn-carousel-auto-slide="0.3" rn-carousel-pause-on-hover >
<li ng-repeat="slide in slides">
<img ng-src="{{eventBasePath}}{{slide.img}}?rand={{rand}}"/>
</li>
</ul>
I'm trying to modify its code so that it does not slide to the next image unless the current image is fully loaded (to avoid the situation of a fast slider half loading images and moving to the next). I'm stuck on how I can trap this image loaded event inside the directive. Doing element.bind("load") does not work as the directive is not applied to the image element.
Thank you!
Check this working demo: JSFiddle
Use anguler.element to find the img element and bind the event.
In your directive, it should be element.find('img').
angular.module('Joy', [])
.directive('hello', [function () {
return {
restrict: 'A',
link: function (scope, ele) {
var img = ele.find('img');
console.log(img);
img.bind('load', function () {
console.log('img is loaded');
});
}
};
}]);
HTML:
<div ng-app="Joy">
<div hello>
<img ng-src="https://www.google.com.hk/images/srpr/logo11w.png">
</div>
</div>
Update 1
If use ng-repeat, add a $timeout to wait for ng-repeat finishes first. Check working demo: JSFiddle.
Note: this demo is loading a very large image. After the image is loaded, the text img is loaded will be shown.
Update 2
If the event binding is not working, try native image.onload binding (or oncomplete to find images cached). Working demo: JSFiddle.
Hi guys I am using this http://www.jquery-steps.com/Examples as my wizard form plugins.
I notice that it has a conflict with Ckeditor plugin with an error of Uncaught TypeError: Cannot read property 'unselectable' of null.
I just tried the solution on this post Ckeditor with jQuery form wizard but it doesn't fix the issue.
What is the best solution for this?
I guess you put the CKeditor right into the wizard HTML code. In that case what´s really important to understand is that jQuery Steps manipulates DOM objects. That´s really bad for javascript code in general.
To run javascript controls within jQuery Steps you have to ensure that:
no javascript code goes inside your wizard HTML
first jQuery Steps code executes and then the javascript code that belongs to the HTML inside the wizard HTML
Good example:
<script>
$(function ()
{
// first jQuery Steps
$("#wizard").steps();
// then components inside jQuery Steps
$("#editor").ckeditor();
});
</script>
<div id="wizard">
<h1>Title</h1>
<div>
<div id="editor"></div>
</div>
</div>
Bad example:
<script>
$(function ()
{
$("#wizard").steps();
});
</script>
<div id="wizard">
<h1>Title</h1>
<div>
<script>
$(function ()
{
$("#editor").ckeditor();
});
</script>
<div id="editor"></div>
</div>
</div>
Cheers,
Rafael
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>
I'm using snap.js with AngularJS using the angular-snap.js directive.
https://github.com/jtrussell/angular-snap.js
I'm also using Andy Joslin's angular-mobile-nav.
I'm wondering where I should store the code for the menu:
<snap-drawer>
<p>I'm a drawer! Where do I go in the angular code?</p>
</snap-drawer>
Because this isn't a unique page within the angular-mobile-nav, I'm currently putting the on every page and just using a directive that contains all my menu code/html.
Seems like this could be inefficient as it is loading a new directive on each page, right? Any idea on how to do this better?
Thanks!
So this is what I've done (I also use angular-mobile-nav and angular-snap.js).
This is my HTML Code
<body ng-app="MyApp">
<div snap-drawer>
<ul class="list">
<li ng-repeat="item in sidebar.items" ng-i18next="{{item.name}}" ng-tap="snapper.close();go(item.link)"></li>
</ul>
</div>
<div id="container" snap-content snap-options="snapOpts">
<div mobile-view=""></div>
</div>
</body>
please note that go() is the function to change the page and that I'm using ng-i18next to translate my items. Also ng-tap is a directive which listens for touch events instead of mouse events. With Angular >1.1.5 there's a mobile module, so my ng-tap directive won't be needed anymore.
And by using $rootScope I can put items in the sidebar:
$rootScope.sidebar = {
items: [
{
name: 'item_one',
link: 'page_one'
},
...
]
};
So if you want to change the items in the sidebar, simply override $rootScope.sidebar (not $scope.sidebar) in your controller ;)
If you don't like two animations happen at the same time, you could write a function, which waits for the sidebar to close and then change the page. It could look like this:
$rootScope.waitThenGoTo = function (page, time) {
time = time || 200;
$timeout(function () {
$navigate.go(page);
}, time);
};
If you have still question, please comment. I'll try to update this answer as soon as possible.