Situation:
I am trying to include a partial with ng-include without the need for any routing. I just want to include a specific partial of many dynamically. This is more or less how it looks like:
<div ng-controller="SomeController">
//This controller defines a $scope.getPartial(id)
<ng-include src="getPartial(something.id)"></ng-include>
</div>
It works, the partial is included. But looking at the console I can see that the controller is called several times and the first time it is called, I get a 404
GET path/to/partials/undefined.html [HTTP/1.1 404 Not Found 162ms]
it seems that something.id is not defined, when the include is made for the first time.
Questions:
how can I simply include a partial, without creating a new scope?
if that's not possible, how can I make sure, the controller is called only once?
and how can I avoid the 404?
I'm fairly new to AngularJS and may therefore make wrong assumptions about stuff or miss obvious things, please enlighten me.
ngInclude creates a new scope by definition so you can't easily circumvent it. And, since nested scopes inherit from each other your newly created scope will be able to read whatever is in your SomeController, so you shouldn't have any problems with new scope.
ngInclude's src attribute will get re-evaluated on each $digest scope, so you can't stop it from calling your controller's method repeatedly. For that matter you need to make sure your method is light and fast and it returns the same output given the same input
You may avoid initial 404 by returning empty string "" when id is not yet defined:
$scope.getPartial = function(id){
if(!id){
return "";
}
...
}
Related
I use a same controller twice on my page, with same data, but i display it not in the same way (not same parts of the array).
I can see with ng-inspector that the scope is correctly updated when i perform some change, and it's duplicate. But in the view, it's doesn't change ! A simple param to false by default and passed at true with a simple timeout, in the view, it's always to false.
If i display only one time the ng-controller, the view it's updated.
How to correct that ?
Ok, so you didn't actually give much in the way of code, but this sounds like you're falling victim to one of the classic scope blunders. The first thing to try is to use properties of objects, not primitives on the scope.
This means instead of using this code ---
$scope.myBool = false;
{{myBool}}
use this code ---
$scope.myBool = { value: false };
{{myBool.value}}
The reason being is that Angular likes to do funny stuff like use prototype for new scopes. This means you'll get the prototype of the parent scope, and then when you instantiate your scope, you can override the parents prototype with your new value, without changing the parents value. You get past this by using an object on the scope instead.
The second option that might be occurring is you're doing something that isn't causing an angular digest cycle to happen. You might need to manually kick one off.
Without seeing any code, there's no telling, though.
I'm confused by these three different ways to specify a controller.
1- I can have an include in the app/index.html file:
<script src="scripts/controller/nav.js"></script>
2- I can have an attribute in a route:
.when('/link/list/', {
templateUrl: 'view/list.html',
controller: 'navController'
})
3- I can have an attribute in a view:
ng-controller="navController"
It's quite a few. I wonder which way to go and when.
Kind Regards,
Stephane Eybert
Your (1) has nothing to do with (2) and (3).
And there are other places where you can bind controllers (e.g. a directive's controller property).
Each way is serves a different purpose, so go with the one that suits your situation.
If you have a directive and want to give it a specific controller, use the Directive Definition Object's controller property.
If you use ngView and want to give each view a specific controller (as is usually the case) use the $routeProviders controller.
If you want to assign a controller to some part of your view (in the main file or in a view or partial) use ngController.
All the above are methods for "binding" a controller to some part of the view (be it a single element or the whole HTML page or anything in between).
Im quite new too but Ill try to explain in a more layman way.
1 For each .js file you have (which may contain one or more controller defined), you need a corresponding entry into the script for #1 there. Its not the controller itself, more like to allow script to recognise that this .js file is part of the set of files to run.
2 is more like specify a state or route, which may or may not use a controller. Its much like saying how one event should lead to another. The controller may be involved in the transitions of the states/routes (ie. responsible from one state, to another) or within a view itself.
3 is for using a controller's functions within a view itself.
I've added comments to one of the answers, but aside from syntax this may is more of a design question. Here is my opinion
Firstly, (1) is irrelevant to the conversation.
(2) is the preferred approach when specifying the controller for the view as it decouples the controller from the view itself. This can be useful when you want to re-use the same view by providing a different controller.
If you find yourself using (3),consider making that area into a directive, since by specifying a controller you are indicating that it requires its own logic.
I am having issues with something which might imply I am missing something conceptual. I am using a view with a controller which populates from a couple of different services(asynchronous). Everything works fine the FIRST time the view is navigated to. All the expected elements get populated in the controller and the dom is rendered perfectly. However, the SECOND time a user navigates to the same view, the DOM does not render all the elements. I have put breaks in the code and have confirmed that the data is populated by the service exactly as it was the first time.
I put a break on a click event so I could inspect the scope post rendering. As expected the first time it is rendered, everything is as expected. The second time, despite watching the scope variable get set, when I inspect the scope post rendering the variable is undefined!! I have looked to see the $id of the scope. I can see that the first time the $id of the scope is the same through the whole initialization and rendering. The second time the controller is initializing the same scope but when I inspect post rendering I can see that the scope has changed and is now different from the one used by the controller to initialize. I don't understand what created this second scope OR why the first scope was used to initialize the controller.
So my question(s) to the AngularJS folks are as follows:
1. Are there differences in how Angular behaves from the first and then subsequent route events to the same route. I have noticed that the pages render faster.
2. How do $digest/$apply come in to play? It won't let me call them because I get the inprog error.
3. How/approaches to debugging this? I really don't understand where to go with this. Its as if I have lost the two way bindings.
Thanks.
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.
First and foremost, the plunker: http://plnkr.co/edit/v1uTz5
This is a working demo of the issue I am running into.
I have a ng-include to include a partial.
Inside the partial I have an text input with ngModel AND directive.
The model updates accordingly inside the include, but any interaction outside the include is ignored. The {{test}} outside the include doesn't update, but the {{test}} inside does.
The directive, when called, handles the enter key and calls the correct scope and function. However, the $scope.test variable has never been updated, but $scope.testFinal is updated and the ng-include template renders it appropriately. Trying to reset the $scope.test model does not work either.
Am I missing something here? Or is this a bug with the directive or with the ng-include?
Instead of using a primitiive to define the variable, make it an object.
$scope.model={test:''};
Directives create their own scope for each item. When you equate a primitive to a new scope variable, it has no binding to the original, however when original is an object, a reference is created , not a copy, and changes made in one will reflect in the other
Simple explanatory example:
var a ='foo';
var b= a;
/* now change a*/
a='bar';
alert( b) // is still 'foo'
now do the same with object:
var obj_1= {a:'foo'};
var obj_2=obj_1;
/* now change obj_1.a*/
obj_1.a='bar';
alert( obj_2.a) // change to obj_1 will also change obj_2 and alert returns "bar"*/
Your Plunker Modified
Read this article on angular wiki for more detailed explanation
John Lindquist has a video about it. Although he doesn't quite explains why you need to use an object.
Basically every time there is a new non-isolated scope, every property of the parent scope is copied to the new scope and, as #charlietfl explained, copying a primitive type really creates a "copy" but with objects what you get is a reference, hence the changes are global.
ng-include creates its own scope and it is different than outer scope. Use this.test instead of $scope.test inside ng-include template. It will work properly.