I've tried so many different things here with no luck. Basically i have an array of strings (just sentences). I need to ngRepeat over those and output them. No problem. But I need to be able to do a find and replace for a particular word combination ("social security" in this case), wrap it inside a link and add an ngClick directive to that link.
I'm pretty certain that i need to use a directive and this has something to do with compiling and linking. But Thats about as far as i can get with this. Any help would be most appreciated.
I think you could use something like this : http://jsfiddle.net/DotDotDot/Z9CHL/2/
I'm not sure it's the best implementation, but at least it works
The HTML code is quite simple, I send the custom directive one parameter, the full sentence
<ul ng-repeat='sentence in list'>
<li><span the-dir txt="sentence"></span></li>
</ul>
On the javascript side, it's quite easy but longer :
.directive('theDir', function($compile){
var r=/(social security)/ig;
return {
scope:{txt:'='},
link:function(scope,element,attributes){
scope.aFunction=function(){
console.log('In the directive, you clicked on social security');
}
if(r.test(scope.txt)) {
splitted=scope.txt.split(r);
console.log(splitted)
var newSpan=new angular.element('<span>');
for(var i=0;i<splitted.length;i++)
{
if(r.test(splitted[i])){
var newLink=new angular.element('<a>');
newLink.attr('ng-click','aFunction()');
newLink.addClass('social-security');
newLink.html(splitted[i]);
newSpan.append(newLink);
}
else
newSpan.append(splitted[i]);
}
element.append(newSpan);
$compile(newSpan)(scope);
}
else
{
element.html(scope.txt);
}
}
}
});
I use a Regular Expression to find all occurrences of your sentence 'social security', split the full sentence on these occurences, then replace each time the words by a new angular.element (an HTML link in this case, with a ng-click attribute). I append all the regular words and the links to the original element, and then $compile the newly created span with the working links in order to have a working ng-click.
And it seems to work
I let you dig in the code, and I hope it helps you
Have fun
Related
I have a question about the custom directives in AngularJS.
I have a list in my scope, a list of objects that I use to create tr lines in a table with ng-repeat. The tr line is a custom directive.
I have something like :
<tr custom-tr ng-repeat='element in list'></tr>
As the list is populated via an HTTP call, the list is populated little by little as I have a foreach for each result in my JSON response :
promise.then(function(json) {
json.data.foreach(function(response) {
other_promise.then(funtion(element) {
$scope.list.push(element);
})
})
})
So as the $scope.list gets new elements little by little, my table is supposed to display a new line each time, but actually it waits the end of the foreach before rendering the TR.
When I look at the network tab of the inspector I notice that the HTML template of the tr line is loaded AFTER all the promises.
Is there a way to pre-load the template so I can see the lines in my table before all the promises are done ? Or should I do that differently?
Thanking you in advance,
About the first solution (ng-if), I thought about it some time ago but even if the list is not empty (at least one element), it doesn't mean the html template is loaded as it is loaded at the very end. You can have 35 element, if all the promises are not done it won't work.
I tried the second solution with a similar NPM package and it seems to work now.
Thank you!
I'm using ngTagsInput in which I have a 3 tags one, two, three.
When i click on remove one the fields shows one, two rather than two, three.
I've checked the scope variable and I can see the correct two, three.
So I've tried to literally clear and reinstaniate the scope variable however no luck.
Am i missing something?
HTML looks like:
<tags-input ng-model="detail.tagsToAddField" add-on-space="true"
placeholder="." on-tag-removed="detail.tagRemoved($tag)">
</tags-input>
and in the controller, i'm literally trying to reinstate but it makes no difference to the tags in the field.
self.tagRemoved = function($tag) {
var current = $scope.detail.tagsToAddField;
$scope.detail.tagsToAddField = [];
$scope.detail.tagsToAddField = current;
};
Anyone used this ngTagsInput?
Thanks.
on-tag-removed is used to fire a callback when you're actually done removing the tag from the input, you really should not modify or rearrange your tag list in that function except for extreme cases, because your tag was already removed from the list at that point. so "reinstating" won't do anything
Because you didn't post much of any code that is useful for fixing your issue. Here is a plunk (forked from the demos) with your use case working properly.
In the talk creating content container components with web components and Angular (specifically linked to the time where it is relevant to this question) ng-conf 2015 they talk about directive components creating and matching multiple insertion points. (creating behavior that matches web component insertion implementations). Good thorough 20 min talk, but they seem to leave it hanging slightly. For example,
// this being the html page
<my-site-container>
<div t-to="menu">
this displays in the menu/nav of the directive component.
</div>
<div t-to="body">
this displays in the body/main of the directive component.
</div>
</my-site-container>
// directive template
`<div id="site-container>
<nav t-id="menu"></nav>
<main t-id="body"></main>
</div>"`
which needs custom code in the link function. (transclusion using just ng-transclude does not allow 'matching' of insertion points. See video for more.) The questions are to their link function code in the DDO:
return {
transclude: true,
template: ...,
link: function(scope, elem, ctrl, transclude) {
transclude(function(clone) {
angular.forEach(clone, function(cloneEl)
var tId = cloneEl.attributes["t-to"].value;
var target = elem.find('[t-id="' + tId + '"]');
target.append(cloneEl);
});
}
};
This did not work completely for me. Here is the plunk.
Question 1. What is best way to filter out the elements with a t-to attribute?
the var tId = cloneEl.attributes["t-to"].value; is undefined when you have traditional markup structure meaning
// the forEach supplies empty-like node, <div..., empty-like node, <div...
<my-directive>
<div t-to="menu">I render in the menu.</div>
<div t-to="body">I render in the body.</div>
</my-directive>
// this forEach supplies correct and only the divs needed
I render in the menu.I render in the body.
// so seems like traditional html structure when iterated will add the space in as empty text nodes.
I used if (cloneEl.attributes) {var tId = ...} in the plunk and that seemed to work
Question 2: What is best way to get an element in the directive template that has a certain name and attribute value?
var target = elem.find('[t-id="' + tId + '"]');
This code does not seem to make sense that it would work and well it is not for me. (note: elem is equivalent to the template or temp as they seem to have it in their code example.)
the find method how are they using it? They seem to be lookinf for the element by attribute name and value. I looked around for the [] syntax and couldn't find any reference to that. Angular limits the find to tag name. And they make no mention of jQuery but jQuery does not seem to have that feature either anyway.
You need to import jquery in your index before angular. After that, find() should work.
I had a problem with my code and by playing a little I found a solution but I have no idea why is it working. Here's the thing:
// recompile all audio buttons
angular.element(textAsDom).find('sound-button').each(function(key, element) {
$compile(angular.element(element))(scope);
});
I got a raw html as a parameter, and inside it I'm looking for directives named "soundButton" and I compile them in order to use their attached scope functions and properties.
However, my first solution was to do it like this:
// recompile all audio buttons
angular.element(textAsDom).find('sound-button').each(function(key, element) {
var compiledSound = $compile(angular.element(element))(scope);
angular.element(element).replaceWith(compiledSound);
});
So I first compile the soundButton and link it to the scope and I got a bound element in return. Then I replace the original element with the bound element. But then all angular dirty checking nor event handlers don't work anymore.
As for now my problem is solved, however for the sake of understanding why it didn't work, I'm asking you guys if you can shed some lights about this esoteric stuff.
Thanks
Sounds like you're approaching this a little weird.
Why not just wire up a directive to sound-button? No need to do .find.
.directive("sound-button", function(){
return {
class: 'C',
replace: true,
....
}
});
I have an array of items bound to <li> elements in a <ul> with AngularJS. I want to be able to click "remove item" next to each of them and have the item removed.
This answer on StackOverflow allows us to do exactly that, but because the name of the array which the elements are being deleted from is hardcoded it is not usable across lists.
You can see an example here on JSfiddle set up, if you try clicking "remove" next to a Game, then the student is removed, not the game.
Passing this back from the button gives me access to the Angular $scope at that point, but I don't know how to cleanly remove that item from the parent array.
I could have the button defined with ng-click="remove('games',this)" and have the function look like this:
$scope.remove = function (arrayName, scope) {
scope.$parent[arrayName].splice(scope.$index,1);
}
(Like this JSFiddle) but naming the parent array while I'm inside it seems like a very good way to break functionality when I edit my code in a year.
Any ideas?
I did not get why you were trying to pass this .. You almost never need to deal with this in angular. ( And I think that is one of its strengths! ).
Here is a fiddle that solves the problem in a slightly different way.
http://jsfiddle.net/WJ226/5/
The controller is now simplified to
function VariousThingsCtrl($scope) {
$scope.students = students;
$scope.games = games;
$scope.remove = function (arrayName,$index) {
$scope[arrayName].splice($index,1);
}
}
Instead of passing the whole scope, why not just pass the $index ? Since you are already in the scope where the arrays are located, it should be pretty easy from then.