So I'm currently facing logical problem problem on how to store data in Vuex.
<ul>
<li v-for="category in sub_categories" #click="setProductCategory(category);">
<span v-bind:class="{active: category == product.category}"></span>
<a>{{ category.name }}</a>
</li>
</ul>
<p class="resultObject" v-if="product.category">
<span class="active">{{ product.category.name }}</span>
</p>
The category object contains a lot of data about category such as icon, title, path and etc. and the product object will be sent to the server so basically it need only category.id property.
So my question is should I store whole category object in product in Vuex or just #click="setProductCategory(category.id);" and then do some extra stuff to show category name ?
It depends on your situation, if category object is large & then repetition of the same category records along with products. So it's better to keep flat structure small objects in store.
Related
I'm creating a bare-bones blog with Angular 5 and a Firestore database. I'm able to save to the database and retrieve the data for display. The problem is when I save the text area value to the database. It doesn't retain the line breaks and spacing I would want and therefore upon retrieval, it's just a single line string. I'm keeping it super simple until I work out these types of issues I'm unfamiliar with.
Any suggestions a simple and effective way of fixing this issue?
This is how I'm currently displaying and saving the data.
ngOnInit() {
this.createForm();
this.postCollectionRef = this.afs.collection('posts');
this.postCollectionList = this.postCollectionRef.valueChanges();
}
addPost(val) {
this.postCollectionRef.add({ title: val.title, body: val.body });
}
<div>
<ul>
<li *ngFor="let post of postCollectionList | async">
{{ post.title }}
<br> {{ post.body }}
</li>
</ul>
</div>
To me this looks like an HTML/CSS issue -- <li>, like most of HTML elements, collapse whitespace sequences by default. Try wrapping {{ post.body }} inside a <pre> tag, or add style="white-space: pre;" to <li> elements.
I am using AngularFire for a project. My firebase Object is of the form
{
mainKey: {
key1:value1,
key2:value2
},
mainkey2: {
key3:value3
}
}
I have entered the data in such a way that I need to display both the value and the key in different columns of a table row.
I am able to display value using {{key}} expression but what should I do if I have to display the key itself?
Is storing data in this way a wrong practice?
You can use the $id property.
<ul>
<li ng-repeat="message in messages">
Message data located at node /messages/{{ message.$id }}
Message text is {{ message.text }}
</li>
</ul>
I've found the way to do so in angularJS docs
<ul>
<li ng-repeat="(key,value) in object"> {{key}}> </li>
</ul>
This would give me all the keys listed.
I have an array of ints in my model which is meant to represent the indices of a list of options the user will pick from using a select list. The idea is there's a list you can expand and you keep picking things from the select list. I have code along these lines:
<ul>
<li ng-repeat="item in model.arrayOfInts">
<select ng-model="item"
ng-options="choice.id as choice.name for choice in common.options">
<option value="">---</option>
</select>
<button ng-click="model.arrayOfInts.splice($index, 1)" type="button">
Delete
</button>
</li>
</ul>
<button ng-click="model.arrayOfInts.push(null)" type="button">
Add
</button>
I have two problems which seem to be related:
the ng-model does not seem to actually bind properly; if I inspect the scope I can see that newly-pushed members of arrayOfInt are still set to null even after I select something from the select menu representing them.
When I attempt to push another item into the array, Angular complains that duplicate options are not allowed.
What is the proper way to do what I'm trying to do?
JSFiddle sample
The first issue ("ng-model does not bind properly") is because you're not targeting the array with ng-model. item in your ng-repeat is a new property on the scope (created by the ng-repeat). It doesn't contain any information on where it came from (arrayOfInts).
The following code tells ng-model to point to the right index of the array, rather than a new scope property.
<select ng-model="model.arrayOfInts[$index]"
ng-options="choice.id as choice.name for choice in common.options">
The second issue is because model.arrayOfInts can have multiple duplicates in it (click "add" twice and you've just added two nulls). Angular needs some way to tell the difference between them, so you need to add a tracking property, in this case $index.
<li ng-repeat="item in model.arrayOfInts track by $index">
I have a products variable, which holds a nested array of hierarchical products, such as:
Product
- cat_id: 1
- name: Some Product
- Children
- Child Product 1
- Child Product 2
- Children
- I can also have more children here
- And another
- Child 3
Product 2
- cat_id: 2
- name: Some other Product
- Children
- A child product
- another
I also have another variable which is an array of products that have been purchased.
What I want to do is to display the full product list as above, but if the user has purchased it, to apply a class.
Here's where I'm at so far:
<ul>
<li ng-repeat="product in all_products track by product.cat_id">
{{ product.cat_name }}
<ul ng-if="product.children.length>0">
<li ng-repeat="l1_child_product in product.children track by l1_child_product.cat_id">
{{ l1_child_product.cat_name }}
<ul ng-if="l1_child_product.children.length>0">
<li ng-repeat="l2_child_product in l1_child_product.children track by l2_child_product.cat_id">
{{ l2_child_product.cat_name }}
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
What I want to do is for each is to apply the class, if the contents of the second array, contains the current product's cat_id, for instance:
<li ng-repeat="product in all_products track by product.cat_id" ng-class="foreach(otherarray as owned){ if(owned.cat_id==product.cat_id){ 'some_class' } }">
I'm still very new to Angular so i'd like to know the proper way of achieving this.
I'm currently porting my application from being purely server side with a static front end, to Angular. I was able to perform this sort of logic extremely quickly using a few nested for-loops with conditional statements in my old app.
However, with Angular, this seems to cause the application to grind down to a very slow pace.
I've heard of dirty-checking, which Angular uses and I think I'm hitting the bottlenecks that occur as a result as my datasets are generally fairly large (around 250 products, with around 40 purchases), but with up to 20 users being shown on a page. What's a good way of avoiding these performance issues when using Angular?
Updated
Here's the code I'm using at the moment:
<div class="row" ng-repeat="user in ::users">
<script type="text/ng-template" id="product_template.html">
{{ ::product.cat_name }}
<ul ng-if="product.children">
<li ng-repeat="product in ::product.children track by product.cat_id"
ng-include="'product_template.html'"></li>
</ul>
</script>
<ul>
<li ng-repeat="product in ::all_products track by product.cat_id"
ng-include="'product_template.html'"></li>
</ul>
<table>
<tr ng-repeat="licence in ::user.licences">
<td>{{::licence.product.cat_name}}</p></td>
<td>{{::licence.description}}</td>
<td>{{::licence.start_date}}</td>
<td>{{::licence.end_date}}</td>
<td>{{::licence.active}}</td>
</tr>
</table>
</div>
This gives the desired output of:
Iterate over the users
Iterate over ALL of the products available
Display a list of all of their purchases (licences)
However, it's incredibly slow. I just timed the page load, and it took 32 seconds to parse all of the data and display it (the data was available after around 100ms).
I'm using the new :: syntax to prevent lots of two-way bindings but this doesn't seem to improve the speed at all.
Any ideas?
Your question is 2 parts:
How do I display products and their children recursively?
In an efficient way, how do I add a class if a product has been purchased?
Displaying Products and their Children
This has already answered well by a previous question on Rending a Tree View with Angular.
Efficiently adding a Purchased class
The inefficiency you currently have is caused from looking through otherarray for every single product.
There are various solutions on how to improve upon this but I think the easiest change for you would to make would be to use an {} instead of an array to track purchased products.
{ cat_id: true }
For more information on why using an Object or Hash is faster looking at this question on Finding Matches between Arrays.
Combined Solution
Displaying Products and their Children
<script type="text/ng-template" id="product_template.html">
{{ product.cat_name }}
<ul ng-if="product.children">
<li ng-repeat="product in product.children"
ng-include="'product_template.html'"
ng-class="{ purchased : product.purchased }"></li>
</ul>
</script>
<ul ng-app="app" ng-controller="ProductCtrl">
<li ng-repeat="product in all_products"
ng-include="'product_template.html'"
ng-class="{ purchased : purchasedProducts[product.cat_id] }"></li>
</ul>
Effiecntly adding a Purchased class aka. otherarray -> purchasedProducts object
I don't know exactly where otherarray is being constructed but a simple conversion would go as follows:
var purchasedProducts = {};
for (var i = 0; i < otherarray.length; i++) {
var cat_id = otherarray[i];
purchasedProducts[cat_id] = true;
}
Remember that ng-class can be a function call.
Starting with Angular 1.3, there is native Bind Once support. when iterating over a large number of items, you can eliminate the watchers on static elements which will not change.
For Example:
<li ng-repeat="product in ::all_products track by product.cat_id">
Will iterate through the all_products array once to populate the ngRepeat, but will not continue to be bound to $watch for change tracking. Smart use of :: can drastically improve performance.
Also, converting ng-class= to a function instead of an inline expression evaluation can improve performance and give you greater control over your output.
I am printing a list of food types by making an API call per category. The food in the categories have this JSON structure:
{"uid":"56",
"title":"Nussbrot",
"code":"X 39 2000000",
"final_factor":"0",
"sorting":"0",
"unit":[
{"title":"Scheiben",
"gram":"45",
"max":"100"
},
{"title":"Messerspitzen",
"gram":"250",
"max":"12"}
]
}
I am looping through & printing the values out into a template. No problem. I am printing the "unit" values into a select box:
<option ng-repeat="title in food.unit">{{ title.title }}</option>
And I am currently printing out the grams & title of the first unit in each food like this:
<div class="max">Max is: {{ food.unit[0].max }}</div>
<div class="grams">Grams is: {{ food.unit[0].gram }} </div>
How can I make this dynamic, so that I am printing out the max & grams of the currently selected unit?
Here's my Plunkr.
Angular makes dealing with options and selected options very easy. You should stop thinking in terms of indexes or value. With angular you can bind the entire object, so there's no need to look it up. For example you could do the following for your select:
<select ng-model='selectedUnit' ng-options="unit as unit.title for unit in food.unit"></select>
Let me briefly explain the expression for ng-options
unit in food.unit means we will iterate over the food.unit array storing each value in unit as we go along.
unit as unit.title means what we are putting in the ng-model whenever the user selects an item is the entire unit object itself. The as unit.title tells angular to use the title of the unit as a display for the option.
What this ends up doing is that whenever the user selects one of the options, the entire unit object will be stored in the ng-model variable (in this case selectedUnit). This makes it really easy to bind it elsewhere. For example you can just do:
<div class="unit">Unit is: {{ selectedUnit.title }}</div>
<div class="max">Max is: {{ selectedUnit.max }}</div>
<div class="grams">Grams is: {{ selectedUnit.gram }} </div>
In angular, if you find yourself dealing with indexes or ids and then looking things up by id or index then you are typically doing it wrong. One of the biggest advantages of using angular is how easy it is to deal with objects, and you should really take advantage of it.
For example, I often see newbies doing something like
<li ng-repeat="person in persons">{{person.name} <a ng-click="savePerson(person.id)">Save</a></li>
And then in their code they use the id to look up the person from an array:
$scope.savePerson = function(id){
var person = persons[id];
$http.post('/persons/'+id, person);
};
This kind of lookup is almost always unecessary with angular. You can almost alway just pass the person right away:
<li ng-repeat="person in persons">{{person.name} <a ng-click="savePerson(person)">Save</a></li>
And then have the click handler take the person:
$scope.savePerson = function(person){
$http.post('/persons/'+person.id, person);
};
I know I strayed a bit from your original question. But hopefully this makes sense and helps you write things more simply using the "angular way"
Her is the plunkr for your example:
http://plnkr.co/edit/lEaLPBZNn0ombUe3GPa9?p=preview
you can fist of all handle the selected item with the ng-selected:
http://docs.angularjs.org/api/ng.directive:ngSelected
<select>
<option ng-repeat="title in food.unit" ng-selected="selectedIndex=$index">{{ title.title }}</option>
</select>
<div class="max">Max is: {{ food.unit[selectedIndex].max }}</div>
<div class="grams">Grams is: {{ food.unit[selectedIndex].gram }} </div>
This should propably work ;) Havn't tryed it yet!