AngularJS: how to prevent nested HTML from being compiled against a scope? - angularjs

Say I wanna have the popup directive with ability to declare it's content within a same HTML view:
<a popup>
<span>Click me</span>
<popup-content>
<div with-some-other-directives></div>
</popup-content>
</a>
I've tried:
At popup directive, modify HTML during compile phase: get the content of the <popup-content>...</popup-content> element and then remove it completely. This way there are 2 problems:
with-some-other-directives compiles and links.
If I have an multiple <a> elements within <popup-content> I see some strange behavior. Suppose this has something to do with nesting an <a> inside an <a>.
Making a separate popup-content directive, that has own compile phase handler. There, I could either access controller of parent popup directive and set the HTML there then self remove it.
Either way I get compilation and execution of with-some-other-directives directive.
Now I have 2 questions:
How to prevent HTML code within <popup-content> from being executed?
Are there better alternatives?
EDIT
The final goal is to use HTML code taken from popup-content, compile it against the scope of popup directive and append to BODY.

Related

Angular custom directives are failing in w3 validation

When I use custom directives of angular, the html page fails in w3 validation. Help me to overcome this issue.
Eg:
<div>
<share-news news-title="{{...}}" news-content="{{...}}"></share-news>
</div>
When I use the above code,
I am getting the error like
Element share-news not allowed as child of element div in this context. (Suppressing further errors from this subtree.)
While the initial source of the page body (before angular processes an ng-app element) may not adhere to the W3C standards, if you use "replace: true" in directives, custom elements are replaced by a template HTML, which can be valid. So, in this case, you can think about an angular element as just a placeholder that is replaced with the terminal HTML output.

Decompile angular elements

We have a angular grid written by some guys here at work, the entire company uses it.
A td-cell could look like this
<td typeahead-cell="location as location.Name for location in getApiLocations($viewValue, mapping)" ng-model="mapping.selectedLocation">
{{mapping.getLocationNames()}}
</td>
The typeahead-cell directive will execute some custom code on the td, what it does is hookup some code so that if you double click or write in the cell it will go from display only to (in this case) typeahead. It does this by taking the html in the td cell (The td cell is already compiled by angular), wrap it with some custom code that does above functioanlly and then call $compile on the entire thing. This works with expressions above like {{mapping.getLocationNames()}} because they do not change when compiling so it can be compiled any number of times.
The problem I face now is that I try to use a more complex expression with ng-repeat. Problem is the first compile (Done directly by angular-core) will change html from example
from
<span ng-repeat="location in mapping.locations">...</span>
to
<!-- ngRepeat: location in mapping.locations -->
Then when our custom grid code executs it will try to compile the code above which will result in an empty since it compiles against a html comment.
This is the code that breaks
$element.html($compile(displayElement.html($element.html()))($scope));
$element is the td-cell that contains my orignal code that, when doing $element.html() it will take compiled code and try to use that. Wont work. Displayelement is a wrapper that will show when we are in displaymode.
I either need to decompile $elementbefor edoing $element.html or somehow move the content of the $element (td cell) compiled and hooked up.
Any ideas?
edit: I have somewhat solved it, doing this
$element.children().appendTo(displayElement);
displayElement.appendTo($element);
This will take the children from the td-cell and add them to the displayElement without actually breaking the original $compile. jQuery.children cant move <!-- comment --> elements so if you have an expression with ng directives like my repater above you need to wrap it in a dummy element like
<span><span ng-repeat="location in mapping.locations">...</span></span>
Any workaround for this?
Instated of that line if you can check with this
//Store it first on a variable if blank
var html;
if(!html) html = displayElement.html($element.html());
$element.html($compile(html)($scope));
Hopefully it will work. May be you need to manage the scope of the variable.
Final solution is this
$element.contents().appendTo(displayElement);
displayElement.appendTo($element);
It's very important to use contents and not children because childrenwill ignore text nodes which will not include the comments generated by ng-repeat directive.

Evaluating moustache expressions after the page was initialized (dynamic binding)

I have a HTML-Document containing moustache expressions that angular-dart evaluates very well:
</head>
<body ng-cloak>
<ctrlTextElements>
<div id="stage">outside: {{ctrlTextElements.test1('three')}}</div>
</ctrlTextElements>
I want to dynamicaly add some HTML with moustache expression like so:
CtrlTextElements.addTextElement(mousePos.x, mousePos.y);
var div = dom.querySelector('#stage');
HttpRequest.getString("../path/text.html").then((r) {
div.children.add(new Element.html(r, validator: new AllowAllValidator()));
});
The content of the added text.html looks like this:
<div>inside: (not evaluated): {{ctrlTextElements.test1('three')}}</div>
That's the result in the browser:
outside: three
inside: (not evaluated):{{ctrlTextElements.test1('three')}}
How can I reevaluate the moustache expressions inside content that has been applied after the page was loaded?
The problem is that you are mixing jQuery like logic with angular logic here : manipulating the dom 'by hand' is rarely a good solution.
The problem here is that your newly added binding has not been compiled by angularjs = it has not been indexed as a directive that should be watched for and updated when scope changes.
Either you try a more angular way, for example using ng-hide or ng-repeat directive to display your content according to the controllers $scope (or another custom directive), or you try to $compile your newly added directive ( but this is bad ) : https://docs.angularjs.org/api/ng/service/$compile .
Maybe try in your controller :
$scope.$compile( div );
Not sure of the syntax though. Maybe you would need to write
<span ng-bind="..."></span>
instead of
{{ ... }}
to make it work.
#Alexhv is right. Sorry for my previous answer. I assumed it is about Polymer. Was already time for bed.
You can find a code example in my answer to this question: setInnerHtml doesn't evaluate Mustache
The pub package bwu_angular (http://pub.dartlang.org/packages/bwu_angular) contains this code as a Decorator (Directive) named bwu-safe-html

Dynamically change the content of the div using anchor tags angular

I'm having a problem on how to load a dynamic view using Angularjs in anchor tags. By the way I can't use ng-view since ng-view can only be use once in a template. So I'm thinking of using the ng-src but on the sample docs it is using a select element tag and fetching its values to the controllers. What I want is when I click a link say the View1, the content of my div will change. I will explain further.
Say I have this 3 anchor tags
<li>View1</li>
<li>View2</li>
<li>View3</li>
Before
<div data-ng-include="" data-ng-src="default.html"></div>
Now when I click #/view1
//the ng-src of the html will change depending on the link clicked
<div data-ng-include="" data-ng-src="view1.html"></div>
Perhaps you are trying to do something as below:
HTML:
<!-- Dont use # in the hrefs to stop the template from reloading -->
<li>View1</li>
<li>View2</li>
<li>View3</li>
<div data-ng-include="selectedTemplate.path"></div>
JS:
$scope.selectedTemplate = {
"path":"view1.html"
};
ng-view is the main view of any Angular app, and is affected by the route changes. So all you anchor tags will only affect the ng-view template.
To load other partial views based on the main ng-view, ng-include is the correct way to go as you have mentioned already.
To load a view based on the main view (view shown in ng-view), you need to write mapping logic which depending upon the main view should load other partials (ng-include elements for page).
So your partial becomes like
<div data-ng-include='templateNameVariable'></div>
This variable has to be set whenever the ng-view changes on location change.
You can watch for $route $routeChangeSuccess event and change the templateNameVariable based on the active route (hence the view).
So there should a controller out side the ng-view directive which will orchestrate this, and you would do
$scope.$on('$routeChangeSuccess',function(event,current,previous) {
//Change the templateNameVariable to point to correct template here, based on current route.
});

AngularJS: Updating a view with a template from a controller?

I have been working with routing and I have seen how I can update the ng-view using routing and a view template.. But the problem I have is that I am doing a REST call and depending what I get back from the response I wish to update part of the DOM with a view template but I don't want to involve routing.
Does anyone know how I can do this? Or any examples would be great
Thanks in advance
Another answer. Based on your description in the comment, it sounds like you wish to display part of the DOM conditionally.
When you want to display part of the DOM conditionally, you have the following choices:
Use an ng-show and ng-hide directive.
Based on what returns from the RESTful call, you can set up a model that will identify the DOM that needs to be displayed. An example:
<div ng-show="status">
This text will be shown only when the status is truthy
</div>
<div ng-hide="status">
This text will be shown only when the status is false.
</div>
Inside your controller, you could then set the status to true or false based on your RESTful calls and based on which part of the DOM you wish to display post RESTful call.
You can use ng-switch directive
While the ng-show and ng-hide directives will display the content of your DOM conditionally, that is anybody could simply open the source file and see the contents for both, ng-switch directive will load the contents only based on which case fulfills the swtich. An example:
<div ng-switch on="status">
<div ng-switch-when="true">
This text will be shown only when the status is truthy.
Else this is completely hidden and cannot be seen even
when looking at the source.
</div>
<div ng-switch-when="false">
This text will be shown only when the status is false.
Else this is completely hidden and cannot be seen even
when looking at the source.
</div>
</div>
The first child div is shown when the status is true else it is not shown at all. The advantage over ng-show or ng-hide is that the DOM will not contain the child elements if the case is not fulfilled.
$location.path() can be used here.
So, in your Parent Controller, you can make the REST call. Once you have the data with you, you can then decide which route to take. The route value goes into the path() function.
As an example, let us say that if your REST call returns with cherries, you need to take the /foo path (which, based on your $routeProvider will load the template associated with that route). You can then write the following:
$location.path('/foo');
and it will loads the /foo path - $routeProvider will then take care of loading the template associated with that path.
Reference: $location

Resources