Angular binding within binding - html

I'm in the process of refactoring some code written with Angular.js, mostly as an attempt to prevent the braces from rendering whilst everything loads. The original code looks like:
<p class="summary">{{ feature.articleSummary }}<a ng-href="/feature/reader/?articleId={{feature.articleId}}" class="summary">...(more)</a></p>
And then attempted refactoring to:
<p class="summary" ng-bind="feature.articleSummary"><a ng-href="/feature/reader/?articleId={{feature.articleId}}" class="summary">...(more)</a></p>
However, this loses the embedded <a> element. Is it possible to bind an element within a binding? Or would it be better to create a directive to handle a new template? Or is there a better way of handling this?

You should first use ng-bind-html instead of the simple ng-bind and then you should create a filter so sanitise the html:
yourApp.filter("sanitize", ['$sce', function($sce) {
return function(htmlCode){
return $sce.trustAsHtml(htmlCode);
}
}]);
So that your code goes like this:
<p class="summary" ng-bind-html="feature.articleSummary | sanitize">...</p>

Related

Angular2 function call from html element with no load event (or similiar)

I am new to Angular and have run into a problem that seems to have a javascript work around but they aren't very elegant.
I have a model with an array property. I ngfor the list property to build some html selection options. This is all working nicely. The problem comes when I am trying to set default value...the html elements don't have a load event.
I tried numerous html elements and they don't appear to have a load event either but I certainly could be doing it wrong.
I have seen a solution to put javascript tag right after the html and I could do that but I was really looking for a more elegant way in Angular.
I saw this SO post and thought that was my answer but there is a warning given that I agree with and thus it doesn't appear to be a good solution.
Regardless I tried it just to see if it would work and I got:
Failed to execute 'setAttribute' on 'Element': '{{loadDefaults()}}' is not a valid attribute name
<span {{loadDefaults()}} ></span>
So how can I fire an AS2 function in the component to load the default values?
HTML (btw this is NOT a full page load so there is no body tag):
<tr>
<td *ngFor="let loc of locOptions;">
<span>{{loc.text}}</span>
<input type="radio" name="radiogroup" [value]="loc.value" (change)="onSelectionChange(loc.value)">
</td>
</tr>
Edit
I thought perhaps mistakenly that ngoninit would fire too soon...before the html elements are rendered.
So perhaps what is being suggested is that I add a boolean is default to the model and bind THAT as the element is rendered.
In your ngonit function set this.locOptions to your default values. The value can be changed later on in any function and the change will be reflected in the view. Hope this helps you.
You should use ngOnInit to init you data, and call retrieve your data from your component :
defaults : any;
ngOnInit {
this.defaults = loadDefaults();
}
loadDefaults() {
//get data
}
HTML :
<span>{{defaults}}</span>

How to access more than 2 DOM elements "The AngularJS way"?

I'm starting to learn angularJS better, and I've noticed that AngularJS tries to make strong emphasis on separating the view from the controller and encapsulation. One example of this is people telling me DOM manipulation should go in directives. I kinda got the hang of it now, and how using link functions that inject the current element allow for great behavior functionality, but this doesn't explain a problem I always encounter.
Example:
I have a sidebar I want to open by clicking a button. There is no way to do this in button's directive link function without using a hard-coded javascript/jquery selector to grab the sidebar, something I've seen very frowned upon in angularJS (hard-coding dom selectors) since it breaks separation of concerns. I guess one way of getting around this is making each element I wish to manipulate an attribute directive and on it's link function, saving a reference it's element property into a dom-factory so that whenever a directive needs to access an element other than itself, it can call the dom-factory which returns the element, even if it knows nothing where it came from. But is this the "Angular way"?
I say this because in my current project I'm using hard-coded selectors which are already a pain to mantain because I'm constantly changing my css. There must be a better way to access multiple DOM elements. Any ideas?
There are a number of ways to approach this.
One approach, is to create a create a sidebar directive that responds to "well-defined" broadcasted messages to open/close the sidebar.
.directive("sidebar", function(){
return {
templateUrl: "sidebar.template.html",
link: function(scope, element){
scope.$root.$on("openSidebar", function(){
// whatever you do to actually show the sidebar DOM content
// e.x. element.show();
});
}
}
});
Then, a button could invoke a function in some controller to open a sidebar:
$scope.openSidebar = function(){
$scope.$root.$emit("openSidebar");
}
Another approach is to use a $sidebar service - this is somewhat similar to how $modal works in angularui-bootstrap, but could be more simplified.
Well, if you have a directive on a button and the element you need is outside the directive, you could pass the class of the element you need to toggle as an attribute
<button my-directive data-toggle-class="sidebar">open</button>
Then in your directive
App.directive('myDirective', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
angular.element('.' + attrs.toggleClass).toggleClass('active');
}
};
}
You won't always have the link element argument match up with what you need to manipulate unfortunately. There are many "angular ways" to solve this though.
You could even do something like:
<div ng-init="isOpen = false" class="sidebar" ng-class="{'active': isOpen}" ng-click="isOpen = !isOpen">
...
</div>
The best way for directive to communicate with each other is through events. It also keeps with the separation of concerns. Your button could $broadcast on the $rootScope so that all scopes hear it. You would emit and event such as sidebar.open. Then the sidebar directive would listen for that event and act upon it.

Binding dynamically within an ng-repeat expression

For a TV Guide, I am trying to create a dynamic expression within an ng-repeat directive as follows:
<div ng-repeat="programme in programmes['{{channel}}-wed-jan-14']" alt="{{channel}}">
{{channel}} in my controller should evaluate to something like "eTV". The binding is working fine with the alt="{{channel}}" instance but not with the array instance. Angular simply serves up the line of code commented out. If I hardcode the "eTV" string in place of the {{channel}}, it works fine.
Am I trying to ask Angular to do what it is not designed for, or is it possibly my array handling which is dodgy?
Okay, not sure if I just asked a dumb question, but in the absence of responses, I managed to figure out a solution by writing a filter as follows:
Template:
<div ng-repeat="programme in programmes | getChannelDay:channel:dayString" alt="{{channel}}">
Controller filter:
app.filter('getChannelDay', function() {
return function(programmes, channel, dayString) {
return programmes[channel + dayString];
};
});
The issue with my initial problem
<div ng-repeat="programme in programmes['{{channel}}-wed-jan-14']" alt="{{channel}}">
is that I was trying to put {{channel}} inside the expression, but that is the format for markup.
I tried to use the following instead:
<div ng-repeat="programme in programmes['channel + daystring']" alt="{{channel}}">
but I am doing something wrong here. I am pretty sure there is a way to get this to work - if anyone knows, please comment.

AngularJS: Writing to and Reading from textarea with multilines

I can't believe why I can't find anything to this topic ...
I got a form with let's say lastname (input), firstname (input), description (textarea as I want provide several lines). Let's start with the creation of a new object:
Okay, you type something in like
lastname: fox
firstname: peter
description:
what can I say ..
well I'm THE guy
bye
This arrives at my Java Spring MVC Backend Controller as what can I say ..\nwell I'm THE guy\n\nbye which is fine as I can determine where line breaks are.
So, now I want to edit this object. Thus I want to read the stored data and put it in the form. On Serverside I now edited the description text so that I replaced the \n with <br> so that I have HTML breaks.
Now I use angular-sanitize (ngSanitize dependency) and use the directive ng-bind-html="my.nice.description"
If I use this on a DIV, everything works fine, HTML gets rendered and I get my breaks. So this works perfectly:
<span ng-bind-html="my.nice.description"></span>
or one of the following:
<div ng-bind-html="my.nice.description"></div>
<p ng-bind-html="my.nice.description"></p>
BUT as I want to (re)fill my form so the user can edit his previous input, I still use a textarea. Like this:
<textarea ng-bind-html="my.nice.description"></textarea>
And this does NOT work in any way. Which means that I get 's in my text, unrendered.
Though this seems like a ridicilous normal task. It's a form with a simple multiline box, so I want to write my several lines and I want to read them and I want to edit them.
As I am rather a backend guy and not very familiar with HTML and AngularJS I hope that I'm just using the wrong html element or something like this ... Maybe someone can help me out? It's frustrating :(
Thanks in advance and I guess and hope this is not a real hard task :x
store 'br' in your model, so you can use ng-bind-html. add a directive to your textarea which makes the conversion between your $viewVale ('\n') and your model.
.directive('lbBr', function () {
return {
restrict: 'A',
require: '?ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel) {
return;
}
ngModel.$parsers.unshift(function(value) {
return value.replace(new RegExp('\n', 'g'), '<br />');
});
ngModel.$formatters.unshift(function(value) {
if (value) {
return value.replace(new RegExp('<br />', 'g'), '\n');
}
return undefined;
});
}
};
});
<textarea> elements cannot contain other elements (in your case, <br>'s). They are standalone. You'll have to convert the variable-returned-from-server-containing-<br>'s back to \n's, and vice versa back and forth. You can use an angular directive that handles that for you.

Is there a way to allow safe html data bind in polymer?

Is there a way to allow data bind with html rendering in polymer?
For example in AngularJS there is the "ng-html-bind" directive that does the job. I am searching something similar.
Here it follows an example of where I am willing to use it.
<core-tooltip>
<core-icon icon="info-outline" size="30"></core-icon>
<div tip>
{{box.description}}
</div>
</core-tooltip>
Otherwise any suggestion on how to do it differently?
I am loading this data from a json file and I am searching for a general way to allow "safe" html rendering (against XSS).
This has been answered a couple of times:
How to inject HTML into a template with polymer
How to display html inside template?
As suggested in the accepted answer, I associated an id to my tooltip div:
<div id="tipContent" tip>
{{box.description}}
</div>
Then made my element listen to the box changes:
Polymer("nautes-box",{
boxChanged: function(){
this.$.tipContent.innerHTML = this.box.description.chunk(40).join("<br /><br />");
}
});
I hope this answer will eventually be useful :)