I haven't been able to find anywhere else that really helps me understand my Angular problem here but I suspect it has something to do with inheritance that I haven't completely grasped yet.
I'm doing an ng-repeat of a custom directive and seeing strange behavior when attempting to update the collection from a resource. Here is the plnkr:
http://plnkr.co/edit/QXAFvQix8oUiVMlMuEXw
To see what the issue is, click on the 'Finish Registration' button which retrieves the json from the resource and replaces the current collection. What I expect is for the cards to be replaced by updated cards, but what is happening is that the first 2 cards seem to no longer be bound to the scope (or are somehow zombified) and 2 new cards are added. Each subsequent update updates the new cards as expected. What am I missing?
You are running into an issue with the isolated scope accidentally leaking into other directives defined on the same element.
However, this has been addressed and fixed in Angular v1.2.4 in this commit.
Hence, this version (with only Angular updated) works: http://plnkr.co/edit/kufU5fI90j2lj2jdyLBx?p=preview
The way to write it such that it is AngularJS version agnostic is keeping the relationship between the scopes perfectly clear by nesting the directives:
<div ng-repeat="student in currentUser.studentCollection">
<div student-card
student="student"
unit-schools="unitSchools"></div>
</div>
Related
I am essentially trying to create html component blocks that can be used in a WordPress page editor. The issue that I am running into is each block is using the ng-controller directive to get access to the scope of said controller. This is resulting in new instances of the same controller being created which is causing a delay in the blocks showing up (it's also not a practical way of doing this).
The way that I have the app currently setup is to use a short-code linked to one large page of html, which is not easily manageable as an end user and moves away from the component based structure i'd like to implement. I've tried using the angularjs .component() directive but I am running into the same issue of having new controller instances.
<div ng-controller="someCtrl">
--- first html block that loads first ---
</div>
<div ng-controller="someCtrl">
--- second html block that is delayed until after the first is loaded ---
</div>
Essentially, what I would like to know is whether or not there is a way to access the same controller (excluding a wrapper) without creating new instances of said controller?
The answer is no. Each instance of a controller will be a new instance. You could leverage #TheUnknown's suggestion to share data across these instances, as a service is a singleton.
you could use the same controller, by using ng-if.
so when
<div ng-if="loadFirstHtmlBlock"></div>
is true. it will load only that particular <div>.
Same goes for
<div ng-if="loadSecondHtmlBlock">
This is just a simple method you could use.
The end result that I have found as the best way (in angularjs) to fix this sort of issue would be to add different components with their own respective controllers that fetch information from a service. This would then result in much better performance on the page as well as a more 'in sync' type of loading.
I am new to Angular development. I have a list of images which is being retrieved from a remote server and needs to be displayed in two different image carousels. At the moment, I have a setup like:
<div ng-controller="MainCtrl">
<div ng-controller="CarouselCtrl"></div>
<div ng-controller="CarouselCtrl"></div>
</div>
Where the data is retrieved and stored in the scope of MainCtrl, then used in both image carousels. But that ends up with the two carousels linked together, so that any operations on one are reflected on the other.
Plunker example here:
http://plnkr.co/edit/e79x0d2fF5zwx0UwXIxW?p=preview
What is the best and right way to fix this, so that the same underlying data set can be used by both carousels, but their interactions are not tied together?
Edited: I forgot that about the loose way that parameters can be set/defined. Both carousels were using the same parameter (.active) to determine their active slide. So when one changed .active, the other reflected the change. Changing them to .activeA and .activeB de-linked in in the way I needed them delinked.
The reason is that both Carousels are being bound to the same $scope.kittens property, this is two way data binding, changing the index of one, will change the other, the solution is Either to create a Directive with an Isolated scope that will create a carousel that has it is own separate scope, Or if you want to keep it simple, construct another Scope property (kittens2) that is a copy of the first property :
$scope.kittens2 = angular.copy($scope.kittens);
this will create a new scope property that is not data bound with the first one, then, in your second carousel change the ng repeat to iterate to the new scope property that has its own scope now.
<slide ng-repeat="kitten in kittens2" active="kitten.active">
this way you are separating the scopes, this solution is just to keep the example simple, but the right way is to use directives with Isolated scopes.
Note that doing :
$scope.kittens2 = $scope.kittens
will not work because you are binding the kittens2 property again with the kittens property, angular.copy copies the old object to a new object with a different reference.
I have an Angular app with a custom element directive (<uber-table>). <uber-table> is supposed to take a collection of objects, render them into a <table> and add some functionality (click row to toggle the underlying object as selected, search box for live filtering, action links on each row with customized click callback(s) for each object). I created a Plunker with the relevant code. Please note that Plunker is giving an error about map (Object [object Object] has no method 'map'), but locally I am not getting any errors.
The post-link function's element parameter is not the <uber-table> element as I expected. Instead it is the template's <div class="uber-table"> element. This is preventing me from extracting data from <uber-table>. What am I doing wrong? Any help will be much appreciated.
Here's a rundown on some of the issues.
First main issue is you want existing content already within the uber-table markup to exist, as well as a new template. Unless told otherwise the existing content ( columns) in this case will be overwritten. In order to include existing content in a directive that has a template, you need to set transclude:true then identify within template where this existing content needs to be placed using ng-transclude on element that will be parent of the content.
Here's demo with transclude fixed
New problems arise now where you are trying to use jQuery to loop over columns and return angular attrs => column.attrs . This throws undefined error.
I haven't tried to unravel this enough to sort out the columns issues yet. They should likely be handled by directive of their own
UPDATE: here's slightly revised error free version using jQuery to parse column count. I'm afraid am still confused a bit by structure of this. I don't see need to use jQuery to parse colunms, this could be converted to directive or pass column definitions into main directive from controller scope
After trying several things and looking at the documentation again, I finally got it working. The solution was to put the post-link function inside the compile function. Also I had to update my isolated scope to use =, set replace to true and set transclude to 'element'.
I updated Plunker if anybody wants to see the changes. The Plunker version isn't working, but since it is working locally, I'm not going to spend too much time on it.
I have a page that has a number of directives. There are a number of directives in the header/navigation each with there own scope. There is also a ng-repeat of 25 items and each one of those creates a directive each with its own scope.
One of the directives includes a form that includes a custom filter to display form errors, it looks like this:
<span>{{ createProjectForm.name.$error | nagParseErrors }}</span>
Now the concern I have right now is that nagParseErrors is being executed about 33 times when anything in any scope changes even though this data createProjectForm.name is binded to (with ng-model) is only contained in the controller scope and the directive's scope containing the form (which is just being passed to the directive from the controller scope). I know it is related to the number of scopes (or directives) on the page because if I limit the ng-repeat from 25 items to 1, the filter is only called 9 times. This also happend for built-in filters (like json, and it even runs more times).
Is there something I might be doing wrong here or is this in fact how it should work in AngularJS?
BTW, I realize now that displaying the errors might be better off as a directive than a filter I am planning on going the directive route however I would like to clear up my understanding of filters here since I will probably run into this at some point down the road.
This has been addressed numerous times. All AngularJS expressions will be constantly re-evaluated throughout the lifecycle of the app. This is how two-way databinding in AngularJS works.
So, there's nothing wrong with your code. It's just that you need to make sure your filter is idempotent (returns the same output given the same input).
For more info take a look at Why Scope.$apply() calls $rootScope.$digest() rather than this.$digest()? and scope docs.
I'm trying to create a new directive that selects among several children using an ng-switch. This example is not much more than creating an ng-switch inside a directive, but eventually the directive will have more display sugar and some automatic functions so it seemed that creating a directive was the right solution.
My progress so far is in this plunker:
http://plnkr.co/edit/yeCiIOCQswYJHyTozQUZ
The $compile I'm doing seems to be evaluating the switch, and determining that the value doesn't match any of the when clauses which shouldn't be true. You can see that by inspecting the elements in the rendered picker.
I'm also concerned that calling $compile at this stage seems to have thrown away the item list, so it seems like I'm barking up the wrong tree.
How do I get the transcluded content to re-evaluate within the current state?
Update
I think I was barking up the wrong tree. Mathew's answer got me started in the right direction, so it was a big help.
As far as I can tell trying to construct a directive (ng-switch) inside a directive is a bad idea. In the previous plunker when the compile happened the template was changed permanently. That means if I changed the which parameter it wouldn't update. That's what was smelling funny to me in the first place.
Here is a revised plunker:
http://plnkr.co/edit/WUVgdXjwedxO4356321s
In this case, there's a watch on the 'which' value that refires the transclusion. That function removes the previous entry (if any) and adds the new one. There's a couple added benefits.
First I removed the 'item' directive. There's no reason for it to exist, since I'm just looking at the class. Second, I used $animator to do the list manipulation. That means you can add ng-animate to the picker and get animation effects.
I hope that helps someone else looking at this question.
There were two problems with your code:
1) Your template had which being evaluated so your on was becoming the number 1, instead of the variable you wanted which is "which":
template: '<div><div ng-switch on="which"></div></div>',
2) When you used compile, you needed to pass in the $scope like so:
$compile(sel)($scope);
Here's an updated plnkr for you: http://plnkr.co/edit/Q6ViJBvkLwQRgUKYMfS9?p=preview