I am making an address book. Other than basic contact details, each contact also has a field of roles that itself is an array of different roles. They also have a field of permissions, and a field of certifications.
My address-book design is like the normal 2-panel design; on the left is the list of all the names. When you select a contact, the details show on the right:
<div addressbook>
<!-- This is the list of names -->
<div list>
<!-- This is the selected contact -->
<div contact>
Ideally I wanted to pass the list to list using one-way binding. Then the selected contact is two-way binding between addressbook and list and one-way binding to contact:
<div addressbook>
<!-- This is the list of names -->
<div list="{{ list }}" selected-contact="contact">
<!-- This is the selected contact -->
<div contact="{{ contact }}">
I'm afraid the app would run slow if I pass the list as one-way binding since {{list}} becomes a very long string that needs to be parsed to JSON later. I feel that the two-way binding would be faster!
Is this true? Would I run into any performance issues if I keep the code as it is?
In a directive context, it's more helpful to think about the #s and =s as "bind to value of a DOM attribute" and "bind to the value of a scope property" rather than thinking of it as one-way or two-way.
#s are suitable for passing trivial values like short strings or numbers to your directive. When you have something non-trivial like an actual JavaScript object (your list) that you need to reference from within your directive, bind it with =.
Related
I'm experiencing some peculiar behavior based on the <select> tag. It seems that the dropdown is empty depending on where I place it in the template. For example, this works:
<div ng-if="admin.editingRole">
<select ng-options="role.name for role in $data.roles" ng-model="admin.editRole"></select>
</div>
But if I include the exact same element in another place:
<div>
<label for="role">Role</label>
<select ng-options="role.name for role in $data.roles" ng-model="admin.editRole"></select>
</div>
The dropdown is empty. I can't for the life of me figure why this would happen.
It seems that the original developer was using getData() method to populate a $data object from what I can understand, whose scope is only the table contained within the view. Seems to be deprecated and poorly documented so it was a little confusing, and that's why I couldn't access the $data variable anywhere in the view.
I'm trying to go with the best approach and avoid unnecessary rendering/processing time in my AngularJS app when choosing between 2 directives to be displayed in the page inside an ngRepeat loop, want to know which is the best way:
If by setting the ng-if directly in the directive html element, like:
<div ng-repeat="element in list">
<my-directive-a ng-if="someFunction(element)"></my-directive-a>
<my-directive-b ng-if="!someFunction(element)"></my-directive-b>
</div>
Or by moving out the first <div> from the directive's template and use it as a wrapper for each directive. For instance:
<div ng-repeat="element in list">
<div ng-if="someFunction(element)">
<my-directive-a></my-directive-a>
</div>
<div ng-if="!someFunction(element)">
<my-directive-b></my-directive-b>
</div>
</div>
NOTE: The starting <div> element on each directive could be modified behave the same so I will basically take that out of the directive's html and moving it outside the directive declaration in order to place the ng-if there
What would be the best approach for this case? Are there any performance implications from doing it one way or another? Or is it just the same thing? Consider that the number of elements in the list could get really big.
They are quite the same, but you can improve performance with one-time binding, but only when element does not change at runtime (for example, let's say that it has property name, and your someFunction is like return element.name === 'John'). Angular just stop observing this function when it returns value, and watches will be deleted. There are 2 prerequisites to use this solution:
Elements properties in list does not change (if you rely on them in someFunction), for example if you rely on name property name must not change, because watcher on someFunction is note available.
When list changes or its elements properties change, you reload all list (for example, you fetch it from server again if you know that change occurred)
What you get with this? There is no watches after my-directives are drawn on ng-ifs, and when something changes, new reference is bound to list (for example, it comes from server) and everything will be redrawn, ng-ifs will run again and when will become stable (function returns value) then will be unbound. How it looks like? Like this:
<div ng-repeat="element in list">
<div ng-if="::(someFunction(element))">
<my-directive-a></my-directive-a>
</div>
<div ng-if="::(!someFunction(element))">
<my-directive-b></my-directive-b>
</div>
</div>
Two colons before expression. But be aware, that with one-time binding it's easy to mess up - you need to be sure that you test your code enough to be sure it works.
I am working on my first angular directive and still getting my head around the concepts and what's possible with directives. As I've been researching the best way to tackle this problem I haven't been able to identify an example that addresses what I'm trying to do, so thought I would ask for some help from the experts here.
I have an array of objects that are one of three types.
I would like to use the ng-repeat directive to iterate through this array and display the objects on the page.
Each object type has a different view associated with it as each object shares some properties, but also have unique properties.
I would like to set up a directive that displays the correct view based on the objective type.
So the logic would work something like the following:
<div ng-repeat="item in dataset">
<the-smart-directive>item</the-smart-directive>
</div>
One idea would be to have one directive where I determine the templateUrl based on the object type and then have a unique template for each of the objects.
Another idea would be to have a parent directive and then three other directives (one for each object type) and the parent directive would insert the correct object type directive (this is the idea that seems like the better approach, but I'm not sure how to actually implement this idea).
I'd love some help in understanding the best way to tackle this and how to implement. If you could provide some example code that would be wonderful and get me started on the right path.
Thanks for your help!
The way we are using it is with ng-switch inside the ng-repeat.
<div ng-repeat="item in dataset" ng-switch="item.type">
<directive-one ng-switch-when="1">
</directive-one>
<directive-two ng-switch-when="2">
</directive-two>
<directive-three ng-switch-when="3">
</directive-three>
</div>
I want to generate a drop down list, which I will use on different places by cloning the html. Is it possible to generate the dropdown code without the bindings and garbage code like ng-option etc?
If I generate the dropdown list using the code like
<select ng-model="userGroups" ng-options="grp.groupId as grp.groupName for grp in groups" class="form-control">
</select>
and then clone this select using jquery, will the bindings will be copied too?
You can use ng-include to reuse template code. In that case you need to ensure that there is a groups object in your current scope which hold all the options for the select.
You could also wrap it in a directive to dynamically assign a model to the select. That could look like this:
<my-select ng-model="userGroups" my-options="groups" />
As Bixi already said: do not mix angular with jquery when it comes to DOM manipulation. It will cause you problems on all fronts.
Given an object below -
function PersonCtrl(){
$scope.persons = [{name: "Mike", age:21,
occupation:{qualification: "engineer", company:"Intel"}}];
}
and the DOM below -
<ul>
<li ng-repeat="person in persons">
Name : {{person.name}}
<div ng-model="person.occupation">
Qualification : {{person.occupation.qualification}}
</div>
</li>
</ul>
I have a list of persons whose names have to be displayed in the list. Now I will initially load the data without any details, in this case qualification of the person.
When someone clicks on the person's name, I will retrieve the persons details. I would like to just change the model, ie add the qualification details to that person's model, and angular to then create the DOM.
One way to control this is use the ng-show, and set its expression, so that it only shows the qualification div, if and when the qualification object has values. However this will also lead to the details div being created for every person, and thus bad for performance.
Is there a way that the dom is created / destroyed by angular when an expression evaluates to true or false ?
If we want to physically remove / add parts of the DOM conditionally the family of ng-switch directives (ng-switch, ng-switch-when, ng-switch-default) will come handy.
If the detail data is small, and there's no huge cost to getting it, or rules about whether the current user is allowed to see it, then I'd say render it and just hide it initially. Keeping it collapsed just lets the user not think about that detail unless they want it for certain records.
If it's big, or expensive to retrieve/calculate, or there are rules prohibiting some users from seeing certain details, that's different. In that case, I'd render only the "button" to access it, and load the details via ajax when requested.