I'm building a third party widget that will be loaded onto multiple sites. I'm using angular for implementation, with a build script that wraps my angular so it doesn't interfere with any other angular that might be used on the page.
Everything works fine when I use my own custom directives, but when I tried to incorporate a loading indicator, using ng-show, it "sometimes" didn't work. I tracked the "sometimes" down to "on pages that already use angular".
It seems that the page's angular is still data binding my templates when they are inserted into the page. Is there any way I can make my dom a no-go zone for the page's angular?
ng-non-bindable seemed like it might work, but if I use that, I can't bootstrap underneath it.
What you need to do is to bootstrap angular programatically instead of using a tag.
<html>
<body>
// other parts of your site
<div></div>
// your angular stuff
<div id='app' ng-controller="MyController">
Hello {{greetMe}}!
</div>
</body>
</html>
// your controller
angular.module('myApp', [])
.controller('MyController', ['$scope', function ($scope) {
$scope.greetMe = 'World';
}]);
// bootstrap angular programatically/manually
angular.element('#id').ready(function() {
angular.bootstrap(angular.element('#id'), ['myApp']);
});
Without forgetting to use ng-non-bindable too on parts that u don't want to bind ofcoz
So, here is what I have done, which is an abomination. I would much prefer a clean answer, but this protects my dom from the other angular instance.
First, I wrap my entire dom element with ng-non-bindable. This keeps the page-level angular from interfering with it.
Then, I add the following, adapted from AngularJS - how to override directive ngClick
var root = angular.module('rootModule', []);
root.config(function($provide){
$provide.decorator('ngNonBindableDirective',
['$delegate', function($delegate){
$delegate.shift();
return $delegate;
}]);
});
This removes the implementation of ng-non-bindable inside my angular instance. So the page angular will ignore the dom, but my angular won't ignore it. I just hope I don't have to implement ng-non-bindable-really later down the line.
Someone please save me from this horror by giving me a clean answer!
Related
I am study the tutorial from code school, In the directive tutorial they are giving very simple example of custom directive like 1. Element Directive and 2. Attribute directive. So my question is we can achieve the requirement through the controller, then in which senario we will need custom directive.
Example Element directive
index.html
<product-title> </product-title>
Custom Directive
app.directive('productTitle', function(){
return {
restrict: 'E', // E for HTML Element
templateUrl: 'product-title.html'
};
});
product-title.html
<h3>
{{product.name}}
<em class="pull-right"> ${{product,price}}</em>
</h3>
Same thing can be do in controller
index.html
<div ng-controller="ctrl">
<h3> {{product.name}} </h3>
</div>
app.controller('ctrl', function(){
scope.product.name='HP';
})
Simple. Separation of concerns.
Controllers, architecturally aren't meant for HTML manipulation. They are meant for controlling data, stuff that is displayed on your view.
Whilst you are considering it simple to include that code in controller, which actually is, assume that tomorrow morning a change in the requirement forces you to alter the HTML. What follows is a heavy search and replace activity.
Directives generalize your HTML, they define a seperate layer where you can define custom tags and attributes that can simplify the view you're rendering. Stuffing everything in the controller, will slowly ugly out the whole code base, resulting in difficult code management.
By the way, I guess you'll soon stumble upon the fact that why should i use Services / Factories when i can achieve similar things using controllers. ?? You can check out this answer for more details
In this case the custom directives are mainly for the readability purposes. Custom directive allows you to wrap a chunk of HTML into a different file with a specific purpose. You could for example have a set up wizard for something on your web page. Each custom directive could then represent a separate step of your set up wizard. Instead of having a long HTML doc including all the steps.
You should always create a directive when you see a piece of code that can be reuse.. For example, a DatePicker would be created in a directive.. cause its gonna be useful in more than one place..
But even if its simple, all the logic will be at the same place and you can change it easily (only once) whenever you want. Besides, your source`ll turn more readable once you divide it in more pieces..
I'm working with AngularJS.
One of the (technical) requirements is to fetch the "ng enabled" HTML content from the server, in response to a click event, and inject it into a <div ng-app> via JavaScript.
The problem is that the newly injected HTML is not wired, as Angular goes through the compile and link phases only when the page is loaded the first time.
Is there a way to trigger them manually?
Am I approaching this problem in the wrong way? Is there a more idiomatic way to accomplish what I described?
Thanks
There are multiple ways to inject dynamic content into the view in AngularJS. One of the way to inject dynamic content is to use ng-include directive. It can take an endpoint from where to get view.
You can combine it with ng-if to achieve load view on click. For example:
<span ng-if="clicked">
<div ng-include='pathToTheHtml'></div>
</span>
The clicked variable would be false first, on clicking on the button set it to true, this would trigger ng-include to get the content and inject it into html.
If you want finer control then you need to use the $compile service. The html that needs to be injected into the DOM needs to be compiled and linked to the scope using $compile service. This can be done in a directive.
element.append($compile(htmlFragment)(scope))
angular.bootstrap(element, [modules], [config]);
I tried this in my Angular app, but it does not work. So I tried inserting a custom tag(<mytag>) into the head and made the directive work with this by replacing "head" with "mytag".
This however is not really what I intended, because it adds <mytag> to the body instead of the <head>
Does anyone know how to make it work with the head-tag?
I had the same challenge. Make sure that your angular app is initialized on the html tag. Then this solution works out of the box.
However for us this was not an ideal solution. So I modified Zack Boman (tennisgent) https://github.com/tennisgent/angular-route-styles code, so that it could be used anywhere after app initialization.
Renamed the directive to: zbRouteStyles
Modified the restrict to include attributes: restrict: 'EA'
Changed the line: elem.append($compile(html)(scope));
to
angular.element('head').append($compile(html)(scope));
With these changes I was able to add the directive to any tag after my angular app was initialized even the tag that my app is initialized on.
e.g.:
<div ng-app="myApp" zb-Route-Styles>
<div>
I'm really sorry if this has already been answered, but I have no idea how to search for it since it seems so specific.
I've got an AngularJS app using ui-router to dynamically load multiple views into the same <ui-view> element. I want to use Jquery UI's tooltip function on one of the h3 elements loaded dynamically by ui-router, but I don't know where to put the initializer such that it runs after the h3 is in the DOM.
Things I've tried:
Embedding a <script> tag in the HTML partial that calls $("h3").tooltip()
Putting the script tag in the main parent HTML template - with and without a document.ready wrapper
Putting the tooltip() function call in one of the angular js files that's loading the partial (when I changed the line to just alert(), it seemed that this code was running right before the partial is rendered, so there are no <h3>s yet)
I'm very confused as to how I can run arbitrary javascript code on dynamically loaded elements ... I think the first method should have worked. Even when I make the <script> tag just contain a simple alert(), nothing happens.
Any ideas? I'm also happy to provide more information, but I think I can't show the actual code due to an NDA.
may these will help you:
Embed javascript in angular ui-router view
AngularJS: How to make angular load script inside ng-include?
The approach you are taking to get that tooltip working is very wrong. Instead of trying to load a script dynamically inside a view, you should be wrapping the tooltip plugin inside a directive and assigning that directive to your h3 element. Or, even simpler, use Angular UI Bootstrap's Tooltip directive: http://angular-ui.github.io/bootstrap/
Mock code for custom directive:
'use strict';
angular.module('MyModule')
.directive('tooltip', function () {
return {
restrict: 'A',
link: function(scope, element, attrs) {
// initialize jQuery tooltip
// note element is a jqLite object with jQuery methods available
// DO NOT ATTEMPT TO WRAP element WITH jQuery
// ANGULAR CANNOT TRACK jQuery OBJECTS THAT
// ARE NOT WRAPPED WITH jqLite!!!
element.tooltip(...);
}
};
});
I'm using angular-wizard in a project with UI-router, you can see a demo if you click the "Create" button on the right:
http://plnkr.co/edit/XYUamusHru7eV0nvAD5i?p=preview
The wizard works fine separately from the current project, problems happen when I try to integrate it into my app I get this error in the console:
Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: <div class="steps" ng-transclude="">
I think this has to do with the templating logic in UI-Router, or it could be the way the directive is written creates "orphans". Anyone have any ideas on what the problem is?
looks like your NewCampaign.html is using some copy+pasted dom since it has angular comments in there <!-- ngIF ... -->
once cleaning up the template, you can see that the wizard directive isn't being used in that template. The wz-step directive needs to be in the context of a wizard directive since the transclude happens inside the wz-step.
Also, removed the extra ng-app and ng-controller from the template. The controller should be defined along with the state. Check out the fixed plunkr here: http://plnkr.co/edit/c3fcV5RSUMXGIFpk8AbV?p=preview