I've got a small problem. I have a bootstrap tabset within an angularjs app. The tabs are partially generated from ng-repeat and contain data, that is bound to the scope.
Basically, within the controller:
$scope.data = { ... } // Loaded from a factory
Now, when I reload this data (by replacing the old one with the new one) the tabset will be rebuilt by ng-repeat and the view will automatically switch to the first tab.
Is there a way to replace the data in scope without rebuilding everything in the view?
You aren't explicit about the specific data and which of it is used in what ways.
So this will have to be a general answer:
Try to update only the bits that actually changed.
For example do not replace whole objects or arrays, but only updated properties or indices that actually changed. That way only the relevant parts of the GUI will update.
If you must replace objects, you can still help angular keep the connection between objects in the model and dom elements for ng-repeat if you use "track by" in the expression (which is possible only if the element has some unique id that you can use for that).
Another option: Use one time binding for the parts that should change only exactly once when the data is first loaded: See the section "One-time binding" in https://docs.angularjs.org/guide/expression
Related
I make some dom-manipulation inside a ng-repeat. Now I want to reset my DOM to match the model where ng-repeat is looping over. How can I force AngularJS to redraw the ng-repeat content.
Apply doesn't work obviously cause no changes where made to the model.
The whole idea of ng-repeat is that it follows the model. If you want to change the content you should really change the model. As for the dom-manipulations, you should perform those within an angular component or directive and again, make it follow the model.
If you're not doing that, you pretty much might as well not use Angular at all.
If you paste some code, would be great, but anyway for example lets say we have this:
$scope.rows = [1,2,3];
<div ng-repeat="row in rows">
{{row}}
</div>
In order to redraw the same data with ng-repeat you need to somehow change the scope. You can try this after your DOM manipilation
$scope.rows = angular.copy($scope.rows);
Actually you don't need to do any special things to re bind the DOM in angular js. Angular JS supports two way data binding, so when ever your model changes, the DOM object rebinds automatically with modified contents. So all you have to do is rebind your controller variable, that will automatically updates the DOM.
I need to implement a modal window whose contents is generated by the JavaScript controller (the contents is a hierarchical tree of accordions/panels whose structure is unknown till data is received).
The contents of the modal is defined in a view that includes ng-bind-html directive.
When displaying this just as a normal page (i.e. not as a modal) I get the contents and behavior matching the needs.
My problem is that I can't find the way to make it work as a modal.
Is there any limitation to Angular that I'm unaware of?
The same problem I have with another modal that is also in a view, but its contents is built using ng-repeat directive. In this second case, when I try to deploy data into fields, I get an error stating that the element I'm trying to set its value is undefined. It would appear that such attempt is taking place too early and the DOM is still not ready.
This second case also is not working when attempting to use it in a modal.
The problem may lie with the models that you are using in the modal. You may want to make sure the correct models are even being used. The scope may not be the same in the modal window as it is as a pure template. I like to test which scope things are truly in by using the ng-init or ng-if directives, and creating a function to see if it even gets called. So for example:
HTML template
<div class="panel" ng-if="scopeCheck">
</div>
Controller
$scope.scopeCheck = function() {
debugger; //or console.log('here');
}
If that is not the issue, then I'm not sure if you are creating this modal yourself, but if you are I would suggest maybe using the modal directive from UI Bootstrap and seeing if it works with that.
I have an issue that only occurs when I load directives as part of a angular-ui-router state. When I add the directives directly to index.html everything works as expected.
Here's a plunker that demonstrates the issue I'm having:
http://plnkr.co/edit/EzvTOiSzaf6jjCsBKM1e?p=preview
I have a Resource personResource that returns an array of objects.
The Controller personController queries the Resource, and saves the results into $scope.people.
This populates a template list-people.html which is loaded in a directive listPeople.
What I'm having problems with is another directive filterPeople contains radio buttons with options to filter the list.
The expected result is that if I select an option, it will only display those rows where the row's value matches the option value. What is actually happening is that all of the data is displayed no matter what I select
It's a scope issue, your templates have their own child scopes that can't see values in each other, so when you set the personFilter.color in one template, your template using it as a filter doesn't know about it. You can use angularjs batarang or just insert a simple binding to debug these issues (plnkr):
</tbody></table>personFilter json: {{personFilter|json}
To fix the problem you can set the personFilter object on the parent scope and both children will use the inherited object. Just realize that if you set the object on a child scope instead of just a property, it will only set that value on the one child scope and the other scopes will continue to use the inherited object. You should probably put the controller on a parent. Your controller is being instantiated for both the content and sidebar, so you are querying your people twice even though the data isn't used in your sidebar...
I have a simple table of data, created using AngularJS. One of the columns of the table is calculated from a function on the controller.
I have a button on the page that opens a new modal. When I open a modal using UI bootstrap, I get a new isolated scope (child of the root scope), as expected. If, however, I have an input text in the modal any key-presses in this text field automatically invoke functions on the parent scope - even though I can verify that the scope is isolated.
Here is a plunkr of the behavior: http://plnkr.co/edit/JzhxSDcSefDe04Psxq0w
As shown in the example, the third column of the table is calculated with a function called "ageNextYear". When the table is being rendered, this function is called many times as expected (and can be verified in the console log). If however, I open the modal and type some text in to the field, the "ageNextYear" function on the parent scope still gets called (type some text in the input field and watch the console log output).
I'm not sure whether this is intended behavior, or whether I'm doing something wrong. I have tried using dot notation on both scopes, and explicitly passing a new scope to $modal.open, but with no joy.
I can get around the problem (by creating a watchCollection on "people" and updating the table that way - which may be a better way of doing this overall) but wanted to validate whether others have seen this behavior also.
The issue you are experiencing is not related to the scope of the Modal Dialog. The issue is related to the use of a function within an ng-repeat expression. In general, using functions within expressions is a performance issue, but it's a much larger problem within an ng-repeat. according to This excellent article regarding common pitfalls of using scopes,
When using expressions in views or watchers, you should always remember that an expression is called every time AngularJS thinks it is needed. You will not get the best performance using functions, you might even miss some change events.
That means an expression…
within a ng-repeat will be called for each item separately. Additionally, this is used by the repeat directive to determine data changes.
Can be evaluated multiple times in one digest. This can happen when you're using multiple directives or additional scope watchers.
Can be evaluated even if the direct scope seems to be unchanged.
Containing a function will not be evaluated if the return value of the function changes, but only if the function definition has changed.
Your example causes 3 of these 4 to occur.
You repeat the function call for each object in your scope, 3 items = 3 calls to the function.
You add an additional watcher indirectly by calling the Modal Dialog.
Changes to the data in the Modal Dialog's scope causes evaluation of the scope of the controller containing the ng-repeat, even though the data within the ng-repeat didn't change (no way for it to know if the data changed until the $digest is called). Each change to the Modal causes the $digest, which causes another trip through the ng-repeat, and another call for each item in the ng-repeat.
In your case, the logic does not need to run every time the expression will be evaluated. It is better to compute and write the logic into the scope when the logic result has changed. This decouples the logic from the object and the view.
in summary,
Best practices:
DO NOT use functions in expressions.
DO NOT use other data besides the scope in an expression.
DO use $scope.$apply() when applying external data changes.
Simon,
I liked your question and I added watch on the scope and saw the digest cycle is getting called
$scope.$watch(function watchMe(scope) { console.log('Digest watched me!'); });
The following is the fork with the digest.
http://plnkr.co/edit/5PTO1uPFvmLrg7K9vzTm?p=preview
I donot know this is the reason but I think expressions inside the ng-repeat are calling the digest as it tries to evaluate expression on any event on that item.
I think we should evaluate expressions in the model and give the updated model to the ng-repeat to solve the issue.
Is there a different between this.$el.html and this.$el.append when rendering templates? I'm totally new to js, backbone, etc. In the current project I'm working on, I see stuff like
this.$el.append(Project.Templates["template-library"](this.model))
in the outer view. In this case, this template is for a modal view. Then say the modal view has a row for each item to show in the modal view. Then for each of those rows, the template gets rendered like this:
this.$el.html(this.template({ libraries: libraries.toJSON() }));
Is there any difference between the two? And why append() should be used in certain situations, and html() in the other.
For me it really comes down to how you use your views' render method.
Some people like to use render as an extension of initialize, in that they only use it once, when the view first appears on the page, and often call it from initialize. With this style, you can safely use append without worrying about accidentally adding elements twice, because the render won't get run twice.
Alternatively you can design render to be used over and over again, whenever the view's element needs to change in some way. Backbone supports this style well, eg. this.model.on('change', this.render, this);. For this style, append would be annoying, as you'd constantly have to check whether elements already exist before append-ing them. Instead html makes more sense, because it wipes out whatever was there before.
With append a new element will be inserted into the $el, while html will change the content of the $el.
Using .append() will allow you to add or append something to already existing objects. Rather using .html(), it will change the entire object to new one.