I'm having the following problem. I have a search.js controller which uses a template results.html. That template uses a category directive to show all categories that any of the company products might have (as a sidebar). It also uses multiple product carousel directives to show the products of each matching company.
In my search.js controller I define a scope variable named filterCategoryId which starts at null. This variable is bound to the categories directive as well as all product carousel directives. Which makes me expect that a change to this variable in either any of the directives as well as in the search.js controller would propagate to all others using this variable. The variable is used to limit the display of products to only those of that category.
But on my product carousel directive, I have defined a scope method removeFilterCategory() which sets filterCategoryId back to null. It works within its own scope. I can see the effects. But it does not effect the filterCategoryId on the categories directive. I can tell because I use ng-class to set an active class to the list item mentioning the category id to which the products are filtered. But changing filterCategoryId on the categories directive does propagate to the product carousel directives. There is an ng-repeat before the product carousel directives...
In essence. The variable was first defined on the scope of my controller. If I change it on the categories directive, the change gets propagated (after a noticeable while) to the product carousel directives. But if I change it from any of the product carousel items, the categories directive does not reflect the changes. And I don't see why. The product carousel uses a $watch to check filterCategoryId and recalculate its rows..
All directives use an isolated scope with the same variable filterCategoryId and use '=' as scope value.
What could be the issue?
And secondary even if I change filterCategoryId on the categories directive, it takes 5-6sec to reflect changes on the product carousel directives.
I'm feeling I'm missing something here. Either I have an obvious code error I seem to overlook or I'm missing some angular understanding :). Any help in the right direction would be greatly appreciated.
I would suggest explicitly telling AngularJS to register the change using $scope.apply( function(){/*change var here*/}); , see the docs.
There is a nice article explaining the details of Angular's digest cycle: http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
This seems to be the main problem if you are experiencing delays though I also suggest ensuring that the variable filterCategoryId is always an object, so it is never passed as a copy, maybe change it to filter.CategoryId etc.
Related
Given the following code:
http://jsbin.com/uYETOSUM/2/edit?html,js,output
I want to only show the first dropdown first (parent question in JSON). After user have selected an option I want to show one of the two other dropdowns (children in additionalQuestion object in JSON). Which dropdown to show is given in the JSON used to create the form input fields.
To do this I've experimented with ng-show, but I don't seem to find the proper place to put the custom ng-show code. I want ng-show to be like this in the template:
ng-show"showQuestion()"
where showQuestion() iterates through all questions and matches the PreviousQuestionConstraint to see which of the other questions to show based on the value of the selected option in the first question.
Should it be in the Controller?
Should it be in the Directive?
Should it be in the linker function in the Directive?
All attempts so far have resulted in nothing being shown.
After much hassle I've managed to get a working example for you using the &attr isolated scope syntax which provides a way to execute an expression in the context of the parent scope, in your example all the way up to the showQuestion() function on your QuestionsCtrl controller.
http://jsbin.com/EkIqAju/2/edit
I'm building a complex, variable layout with infinite scroll UI in AngularJS.
Think something like Flipboard with a load of repeating data items, each item containing the same thing (title, description, image etc). Each item is pre-loaded into JSON, sorted and then organised into a series of pages, selected from a series of variable layouts. Each page has a variable number columns and each column a variable number of rows.
In order to achieve this, I'm using multiple Directives for each page layout, compiled at run time. Inside each layout Directive, I'm then building a variable number of columns, each of which contains an ng-repeat for the variable number of rows. Each row can be a different Directive, depending on how it needs to be styled. Again, all of this is happening at runtime.
I'm 99% of the way there.
The Directives are being compiled correctly and calling up the correct column Directives, which in turn are compiling the correct number of rows. Once we get to the row/item level, I'm using a Service which brings back the correct Directive for each item. The reason I choose the Service approach is that I want the items to be reused inside other modules.
The problem I'm having, is that once the page layout is compiled, it sets up the columns and then the rows but does not execute the ng-repeat. I need to get the ng-repeat to loop and also call the specific Directives I use for each item.
I think is a problem to do with $compile and what Angular knows about the DOM. Maybe I need to do a $compile or $apply at some stage to get the ng-repeat to kick in and bind the final Directives to the data items.
**
Side note: if I don't use a Service, but simply use define each layout Directive with a templateUri it works perfectly!
I can go with this approach, but ideally, I'd like to get away from downloading a bunch of templates at runtime. Also, by passing parameters into a Service, I can design more layouts, much quicker and easier than having to build individual template files.
I've set up a JS fiddle with quick example of how I'm approaching all of this:
http://jsfiddle.net/joecarney/vE3Ls/6/
Some SO required code just to post this comment:
<div ng-app="app" ng-controller="appPageController">
<div>
<div my-page></div>
</div>
</div>
If you use scope: {} in a directive it creates a new scope so your directives will not have access to controller scope unless you allow it, which you started out doing but appears you then copied and pasted the same thing for all :)
The directive compiling items in ng-repeat had no access to items. I removed scope from directives and used the attrs value to access the attributes
http://jsfiddle.net/vE3Ls/7/
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 have some issues regarding scope in nested directives. I have two directives: column and nested-menu. Their structure can be something like:
column
nested-menu
nested-menu
nested-menu
other content
nested-menu
other content
column
other content
The column has a variable to which all the nested-menu's should react. I have tried to make this work in several ways that I found Googling, such as broadcasting events from the column directive (for some reason, the nested-menu's only saw the event when I broadcasted it from $rootScope), setting a directive controller in column and storing the variable there (I can read it, but I can't $watch it).
It's important that both directives have an isolated scope, as they're supposed to be reusable in several areas, and sometimes even nested in themselves.
I have made a simplified Plnkr of a base structure, that's not working.
http://plnkr.co/edit/1GP7SKacO777og8PysNF
Thank you!
I am not sure how you want this to behave exactly. But here is a plunker that solves the two directives interacting with each other.
The nested-menu only expects column in its parent currently, But you could change it to expect another nested-menu in its parent if you wish.
Here is the working plunker. Hope this helps.
http://plnkr.co/edit/IAn9Ib8sSkQwKx6mpsm5?p=preview
I am using ng-switch to create a filtering input field in the layout. I am switching on the route name and I use this field to filter the data in ng-repeat on each page.
div(ng-controller="NavbarCtrl", ng-switch on="route.current.name")
input.filter(ng-switch-when='offers',type="text", ng-model="$parent.$parent.search.title")
input.filter(ng-switch-when='merchants',type="text", ng-model="$parent.$parent.search.name")
The problem with this approach is that the value that I input on one page stays saved when navigating to the other route and filters data on the other page as well. I am using the inbuilt angular search filter, hence the model names.
Can I somehow reset the value of the filter on route change?
Another issue is that I find this $parent.$parent scope access impractical, but i couldn't find a more elegant way.
Any hints greatly appreciated!
As discussed in the comments, the confusion was around the search property, which is not a special filter in Angular, but just the name of a property that will be used with Angular's filter filter.
Also, $parent is not needed if a property on a parent scope object is used/referenced. I.e., if $scope.search = { ... } is defined in the parent scope, child scopes (but not directive isolate scopes) can access this object via normal JavaScript prototypal inheritance.