Proper way to delete ng-repeat item from child directive - javascript

So, I got the following generic structure:
<list-item ng-repeat="item in collection">
<remove-item>
<a ng-click="remove()">delete</a><!-- this is inside the view.html -->
</remove-item>
</list-item>
I need a proper way to, when triggered, the remove() function remove the full parent of the clicked .
I got this working calling scope.parent().parent()....remove() on the directive, but it is a shitty way to do that =P
Is there a better way? (sure there is).
Please, don't mind this lame question. I am new at AngularJs, and I am having a hard time trying to solve that. =(
Thank you in advance.

There are a few ways to avoid the $scope.$parent.$parent ... problem in Angular. If you are using scopes, you should put any callback or property that you want children to access in a sub-property of the scope. For example, in the parent directive, use $scope.api.remove = function() {...} rather than $scope.remove = function() {...}. Then you can call it in child directive like so:
<list-item ng-repeat="item in collection">
<remove-item>
<!-- remove() is bound to $scope.api rather than directly to $scope -->
<a ng-click="api.remove()">delete</a>
</remove-item>
</list-item>
The other way to do this would be to refactor your code to use controllers rather than scopes. This seems to have become the preferred way of doing things, and it is how Angular 2 works. This pattern is made especially easy by the new component feature in v1.5.0. I won't give a detailed example, but you can read more about this pattern here, and more about components here.

Related

AngularJs (1.5+) dynamic binding

We are developing our own library of AngularJs (1.6.x) components/directives and we would like to have all HTML attributes that are specified on our components automatically and dynamically passed on to the underlying templates, without having to specify every single attribute as a binding.
For example, if I have a component like <my-component> with a template like <div class="my-component"></div> and I write in my HTML:
<my-component ng-required="ctrl.isRequired" ng-change="ctrl.onChange()" ng-attr-disabled="ctrl.isReadOnly"></my-component>
I want my template to render:
<div class="my-component" ng-required="ctrl.isRequired" ng-change="ctrl.onChange()" ng-attr-disabled="ctrl.isReadOnly"></div>
As I said above, we cannot know in advance all attributes, so they can't all be defined in the component bindings. The problem gets more complicated in Angular 1.5+, because replace has been deprecated, so the above template would render something like this:
<my-component ng-required="ctrl.isRequired" ng-change="ctrl.onChange()" ng-attr-disabled="ctrl.isReadOnly">
<div class="my-component" ng-required="ctrl.isRequired" ng-change="ctrl.onChange()" ng-attr-disabled="ctrl.isReadOnly"></div>
</my-component>
And also, when we have nested, deep hierarchies of components, we want the values to be passed down from the parent controllers to the (grand)children, i.e. the ctrl.onChange() method will be defined in the scope of the page controller where <my-component> is defined, but as each component in the hierarchy gets its own scope, as you go deeper, we want the method to still be callable.
I am relatively new to Angular, I hope the above make sense.

Ng-if on directives with two way bindings that override functions

I've set up a directive that will take in a function from the parent controller as one of it's attributes. This function will be replaced by the directives controller and will be accessible by the parent controller by using the passed function. The issue I'm running into is when placing an ng-if attribute on the directive the passed in function is not being replaced anymore. What causes this and how would I go about fixing this issue? I've attached an example
https://jsfiddle.net/dh7jt1zg/1/
<div class="parent" ng-controller="pCtrl">
<h3>Parent - with ng-if</h3>
<div>{{parentHelloWorld()}}</div>
<child ng-if="testBool" rep-fun="parentHelloWorld"></child>
</div>
<br/>
<div class="parent" ng-controller="pCtrl2">
<h3>Parent2 - without ng-if</h3>
<div>{{parentHelloWorld()}}</div>
<child rep-fun="parentHelloWorld"></child>
</div>
Use ng-show instead of ng-if.
ng-if causes element to remove from the DOM.
I think you'd be better off to create a service instead of replacing the function in the child controller. That seems kind of messy to me.
Personally, I'd use a service to register the function and then you can use that same service from the parent controller to call it.

ng-repeat with includes: best performant way?

I am facing a performance issue with Angular (Ionic), and I'd like to know which would be the most performant way to solve this problem.
I get a list of objects from a service that I have to show in my app:
$scope.objectList = [
{
id: 123456,
name: "abcdefg",
state: [1|2|3|4|5|6|7|8...],
....
},
....
];
The list won't never have more than 20-25 objects.
The problem is that the elements in the list, although they are of the same type, they have to be shown in a quite different way. I have a different template for each posible state an object can be in.
I am using ng-repeat this way to show the list. The list won't change so I can use one way binding.
<div ng-repeat="obj in ::objectList track by obj.id">
....
</div>
Inside this ng-repeat I have to include the presentation template of each object, which changes depending on the object state.
I have tried different solutions but don'f find one that improves substantialy the rendering performance.
For example, I've tried this but I'm not sure that binding a function to ng-include is very performant.
HTML:
<div ng-repeat="obj in ::objects track by obj.id">
<div ng-include="getTemplateToInclude(obj)"></div>
</div>
JS:
$scope.getTemplateToInclude = function(obj){
if(obj.state === 1){
return "tmpls/template-a.html";
}else if(obj.state === 2){
return "tmpls/template-b.html";
}
....
};
Any suggestion of how to deal with this problem? Whicch would be the best way to include this different templates in the same list?
Do I have to use the one way binding notation(::) in the included templates or it's enough with the one used in the ng-repeat?
thanks in advance
I haven't used the one-way binding template syntax (::) so I won't speak to that. But based on your comments and original question, I think the best approach would be something along these lines:
create a single item renderer template that will process your repeater items
continue to use track by syntax in your repeater in order to recycle the item renderers
based on the item's type, use a combination of ng-show & ng-hide to display the correct children nested in your template.
<!-- item renderer template.html -->
<div>
<div ng-show="item.type == 'foo'"></div>
<div ng-show="item.type == 'bar'"></div>
<div ng-show="item.type == 'baz'"></div>
</div>
Creating a single template combined with track by in the repeater will reuse the DOM elements, negating the need to pull them from the DOM and then insert new ones.
Using ng-show or ng-hide vs. ng-if is based on the same idea as above. You are toggling the visibility of the DOM elements rather than destroying/creating them.
The reason I argue for this is that in nearly all performance-related tweaking, reusing or recycling objects are usually faster than destroying and then recreating new objects. This is certainly true of Javascript, and while I've yet to test that theory in DOM manipulation, I would bet it too is true. I'm making an educated guess but since you are using the Ionic framework, this should benefit mobile performance as well,
Just keep in mind, given the various permutations of what you could be showing and the number of rows of data, you might only see marginal performance gains. Depending on the number of bindings inside your template, you might reach a point where the performance penalty for Angular's digest cycle to complete is the same as the performance gains from recycling your DOM elements.

Where and how to modify transcluded content?

I want to buid a directive (let's call it "A") that accepts HTML for transcluded content and modify its transcluded content by adding ng-click handlers on it using a custom logic.
I thought that the pre-link would be a good place to do this, but apparently I was very wrong (it seems that the docs suggest against it).
Every "A" directive will accept its own (unique) content, so I cannot do this in the compile phase.
In the link function I am not sure what I can do...
So, has anyone done anything similar?
EDIT:
I forgot to mention this: The handlers for ngClick should be defined on the directive's scope, not the parent scope. I don't know if Angular allows this, but that's what I need.
In your template you should add ng-transclude on the element want to add your custom html to.
your use of the directive:
<attribute ng-click="clickMe()">
<div>
transcluded data
</div>
</attribute >
and in your template:
<span ng-transclude>
</span>
Hope it makes sense :)

angular js input bind value

I have an input like this :
<input ng-model="mysearch.myfield" id="myid"/>
that is bound to a filter
<table><tr ng-repeat="row in list|filter:mysearch">...</tr></table>
If I modify the input value in the GUI, it works perfectly, but if I try to modify its value via javascript/jquery
$("#myid").val("newvalue")
The input value is updated but the mysearch.myfield is not modified
Actually, I have a list that appears on user actions (it does not exist on page load):
<li onclick="changeTheInputValue('newvalue1')">newvalue1</li>
<li onclick="changeTheInputValue('newvalue2')">newvalue2</li>
...
with
function changeTheInputValue(v) {
$("#myid").val(v);
}
And it does not work when I click on an "li" (the input value is updated, ut the mysearch.myfield is not modified)
I also tried
<li ng-click="mysearch.myfield = 'newvalue1'">newvalue1</li>
<li ng-click="mysearch.myfield = 'newvalue2'">newvalue2</li>
...
but it does not work :(
Any idea ?
Thanks,
any javascript executing outside the angulars event loop won't be efective in angular untill you apply it. in order to do that you need to get the relevant scope and $apply the changes, since is not clear, how and why you are modifying the value outside the angular scope there is nothing much i can say except you could do something like
function changeTheInputValue(v) {
$("#myid").val(v);
angular.element($("#myid")).scope().$apply();
}
that should let angular know about the changes, how ever this is a bad design if using angular. there are far better ways to accomplish this same thing w/o having mixed execution scopes. (angular/the rest);
You are modifying the value of the input "#myid" using direct DOM manipulation. AngularJS is not aware of this. If you want both the html and the value of mysearch.myfield to update correctly, you must do so by modifying the mysearch.myfield property directly, either in a controller or via an ng-model binding or something similar.
The main reason this isn't working for you has to do with how AngularJS modifies the DOM. When you use jQuery to modify the DOM, you are circumventing angular. Angular has no way of knowing if you have changed something in the DOM except if you do it directly through Angular itself. In particular, if you are curious, read about the $compile and $digest services.
Hope this helps shed some light on the subject!

Categories