changing the value of the key in a ng repeat - javascript

My website works ok, my question is...
i have a
<li ng-repeat="(key,value) in bpData.slots">
{{key}} Slots: {{value}}
</li>
which works perfectly as it should, but some of the keys have words like weaponsC or weaponsS, again this is reading from the json as it should.... can i change that somehow(without changing the json as its also used for another page) ie weaponsC would display as weapons concussive, weaponsS would display as weapons surface.
does it need to be changed in the js or can i change it some other way?
to see the working page and a live example
www.ahoymearty.co.uk/blueprints-wiki and search using the hull menu.
thanks in advance
wayne

You could use a filter, this way you can handle all kinds of cases.
Example:
angular.module('appName')
.filter('weaponsFilter', function(){
return function(weapon) {
var weaponStrings = {
'weaponsC':'Weapons Concussive',
'weaponsS':'Weapons surface',
...
}
return weaponStrings[weapon]
};
});
Used in your ng-repeat:
<li ng-repeat="(key,value) in bpData.slots">
{{ key | weaponsFilter }} Slots: {{value}}
</li>

Related

Angular.js ng-repeat array shows as json

I have an issue where I'm trying to display an array in Angularjs using ng-repeat but its showing the entire json array and not only the text inside.
This is my array
$scope.problems = [
{
problem: "problem1",
works: [
"a0",
"a9"
]
}
]
And this is where I want to display it
<li ng-repeat="works in problems | filter: searchCard">{{works}}</li>
Now the {{works}} tag shows this in the live document:
{"problem":"probleem1","works":["a0","a9"]}
According to most tutorials and things i've seen its supposed to display the a0 and a9 not the entire json line.
My second question which might be completely different is, how can I display the text correctly but also hide all of them until a person has used the input to search the "works" input field.
when you have objects Array on ng-repeat you have to select that object params; in this sample our object params are "problem" and "works"
also in our object we have "string array" and string array not have params because that we use it directly in this sample {{work}} is object of string array.
<li ng-repeat="item in problems | filter: {problem: searchCard}">
{{item.problem}}
<ul>
<li ng-repeat="work in item.works">{{work}}</li>
</ul>
</li>
you can use the nested ng-repeats like #Maher mentioned. also, in order to hide data until searchCard is typed, you can use ng-if directive in nested ui.
<li ng-repeat="item in problems | filter: searchCard">
{{item.problem}}
<ul ng-if="searchCard">
<li ng-repeat="work in item.works">{{work}}</li>
</ul>
</li>

ng-repeat not showing anything when ng-model is above it in template

I have been teaching myself angular and am currently following this tutorial, https://docs.angularjs.org/tutorial, except I haven't cloned the repo down and am using different data to keep it interesting.
My issue is probably a very simple mistake I am making but I'm still trying to understand the concepts/jargon of the framework which makes it hard to search for an answer.
When my template looks like this ..
<div>
<ul>
<li ng-repeat="electorate in $ctrl.electorates | filter:$ctrl.query | orderBy:$ctrl.orderProp">
<p>{{electorate.electorate}}</p>
<p>{{electorate.mp}} </p>
<p>{{electorate.party}} </p>
</li>
</ul>
<div> Search: <input ng-model="$ctrl.query"/>
<p>
Sort by:
<select ng-model="$ctrl.orderProp">
<option value="party">Party</option>
<option value="mp">MP - Alphabetical</option>
</p>
</div>
</div>
.. I can type in the search input box to filter the ng-repeat list and order them by the sort by options just fine.
But it would look better with the search/sort by options above the data like so..
<div>
<div> Search: <input ng-model="$ctrl.query"/>
<p>
Sort by:
<select ng-model="$ctrl.orderProp">
<option value="party">Party</option>
<option value="mp">MP - Alphabetical</option>
</p>
</div>
<ul>
<li ng-repeat="electorate in $ctrl.electorates | filter:$ctrl.query | orderBy:$ctrl.orderProp">
<p>{{electorate.electorate}}</p>
<p>{{electorate.mp}} </p>
<p>{{electorate.party}} </p>
</li>
</ul>
</div>
.. except while it shows the search/sort by options, the ng-repeat data never appears.
Why is this and how do I make it work in the way I want?
I don't see any big differences between what I have done and the tutorial (specifically around steps 3 - 7) and my guess was it is due to the search/sort referencing the data before it is ready, however in the tutorial it is structured this way no problem.
Here is my controller as well
angular.
module('electorateList').
component('electorateList', {
templateUrl: 'electorate-list/electorate-list.template.html',
controller: function ElectorateListController($http){
var self = this;
self.orderProp = 'party';
$http.get('electorates.json').then(function(response){
self.electorates = response.data;
});
}
});
Thanks and feel free to point me to a duplicate etc if there is one.
Edit: Thanks to Lex for pointing out I was missing a closing tag for the select element, resolving that fixed the issue.
You are missing a closing </select> tag.

Trigger function on a certain element - ngrepeat - angularjs

Good morning,
I'm trying to change the limitTo filter on a certain list, my issue is:
when I click to the trigger who change the filter limit the filter changes on all ng-repeated categories.
my function inside the main controller
$scope.showMore = function(limit) {
if($scope.limitItems === $scope.itemsPerList) {
$scope.limitItems = limit;
$scope.switchFilterText = 'less';
} else {
$scope.switchFilterText = 'more';
$scope.limitItems = $scope.itemsPerList;
}
}
my scenario (I rewrote it in a simplified version)
<li ng-repeat="item in category.items | limitTo: limitItems ">
{{item.title}}
</li>
<li ng-if="limitItems < (category.items.length)">
<a ng-click="showMore(category.items.length)" >Show {{ switchFilterText }}</a>
</li>
Could you explain me what's wrong with me?
I searched how to select a single element to apply the function but I didn't find anything useful
Update:
I found the way to solve my issue in this way:
No functions inside the controller are involved to make this functionality works properly:
<li ng-repeat="category in maincategories" ng-init="limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: limitItems "> {{item.title}}
</li>
</ul>
<a ng-click="limitItems = category.items.length" href>
<b ng-if="category.items.length > maxItemsPerList && limitItems != category.items.length "> Show more </b>
</a>
I'm not really convinced about Angular (I used it in my past and I was impressed by the performance but now I can see logics senseless):
What I learned:
ng-if and ng-click cannot be used in the same content because ng-if creates new scopes so if you put ng-if on top of the "show more" link it will break the code
ng-init cannot be used in the same element of the ng-repeat otherwise the var initialised will not be available inside the ng-repeat block
I think there is another way to do that, maybe more clean but in this specific case I can't do a lot.
ng-if and ng-click cannot be used in the same content because ng-if
creates new scopes so if you put ng-if on top of the "show more" link
it will break the code
Yes, ng-if creates a new scope, but it is possible to mix ng-if and ng-click (and most other directives). To do that, you'll be safer if you always write to atributes of another object instead of a simple variable. It is plain JavaScript prototypal inheritance in play.
<li ... ng-init="category.limitItems = maxItemsPerList">
ng-init cannot be used in the same element of the ng-repeat otherwise
the var initialised will not be available inside the ng-repeat block
True, in the sense that variables are created in the local scope. But again, refer to an object.
I think there is another way to do that, maybe more clean but in this
specific case I can't do a lot.
You don't need to do a lot, it is quite simple to do it right actually.
Some advices:
Use ng-init with care. I know it will tempt us but always try to put logic inside controllers and services;
Avoid assignments inside templates;
Learn how to use controllerAs syntax. It gives you an object to write your models to (the controller), so solves most problems related to scope inheritance;
Do not inject $scope, put your view models inside controllers.
Full code goes like this:
<li ng-repeat="category in maincategories" ng-init="category.limitItems = maxItemsPerList">
{{category.title}}
<ul>
<li ng-repeat="item in category.items | limitTo: category.limitItems "> {{item.title}}
</li>
</ul>
<a ng-if="category.items.length > maxItemsPerList && category.limitItems != category.items.length" ng-click="category.limitItems = category.items.length" href>
<b> Show more </b>
</a>

How to get consolidated results from ng-repeat?

See this plunker.
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
You are now seeing details of <span ng-init="subCats = subCats + ' ' + subCategory.name">{{subCats}}</span>
</div>
This HTML page shows a filtered result from an object. However, I want to display a consolidated result of the names after "You are now seeing details of" like for example, "You are now seeing details of jim tom". This consolidated list should appear after the element which has ng-repeat directive.
How can this be done?
Thanks
I made an updated plunker for you.
Please try to make your example plunker way more reduced to the specific problem in the future as this helps us to help you.
First I added the search binding as filter to the ng-repeat to make the filter workable:
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | filter:{id:search} | orderBy:'id'">
To avoid executing the filter twice you can save the filter result directly into a scope variable by simply assinging it (in my example to subCategorysFilter):
<div ng-repeat="subCategory in subCategorysFilter = (subCategorys | filter:{tags:tag}:true | filter:{id:search} | orderBy:'id')">
I further changed your getAllFilteredNames() method to take a filter object as argument and made it loop through the results, build an array of the names and join them with a , as separation:
$scope.getAllFilteredNames = function(filter){
var names = [];
angular.forEach(filter, function(element){
names.push(element.name);
});
return names.join(", ");
};
This is now called outside the ng-repeat directive:
You are now seeing details of {{getAllFilteredNames(subCategorysFilter)}}
Have fun!
Update
Two possible solutions for getting a multilined output:
1 - You might change the line
<div>You are now seeing details of {{getAllFilteredNames(subCategorysFilter)}}</div>
to
<div>You are now seeing details of <span ng-bind-html="getAllFilteredNames(subCategorysFilter)"></span></div>
Then any html tags within the expression are compiled as html code. But there are meaningful reasons for angular disabling this feature by default. If your objects are editable by users you need to prevent them from breaking your design by escaping all html tags...
2 - But if you do not need to display the cosolidated information within a single string, you might simply use another ng-repeat combined with an <ul> like this:
<div>You are now seeing details of <br/>
<ul>
<li ng-repeat="subCategory in subCategorysFilter">{{subCategoryName}}</li>
</ul>
</div>
Just style your li accordingly to be displayed underneath each other and you're ready to go.
You can do this in your HTML by moving your consolidated list outside of the ngRepeat and calling the filter again:
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
</div>
<div>
You are now seeing details of
<span ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.name}}
</span>
</div>
The drawback to this approach is that you are calling the filter twice. A better alternative would be to set up a $watch in your parent controller and invoke the $filter manually. I.e. Save the filtered results in a scope variable. The benefit is that the filter is called half as many times and the scope variables you set up are visible to the original list and the consolidated list.
app.controller('ParentController', function($scope, $filter) {
$scope.subCategorys = [{...}];
$scope.tag = {...};
$scope.$watchCollection('subCategorys', function(newList){
//if the collection changes, create a new tag
//reference that is a copy of the old one to trigger
//the tag watch listener
if (newList)
$scope.tag = angular.copy($scope.tag);
});
$scope.$watch('tag', function(newTag){
// if tag changes, apply the filter,
// and save the result to a scope variable
if(newTag)
$scope.filteredList = $filter('filter')
($scope.subCategories, { tags: newTag}, true);
});
});
HTML
<div ng-controller="ParentController">
<div ng-repeat="subCategory in filteredList | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
</div>
<div>
You are now seeing details of
<span ng-repeat="subCategory in filteredList | orderBy:'id'">
{{subCategory.name}}
</span>
</div>
</div>
I'm afraid there is no way of doing that except for selecting the subCategory back. Fortunately, there is a pretty elegant 'angular' way of doing that. Add this to your controller:
$scope.getSubCatById = function(someId) {
return $filter('filter')($scope.subCategorys, {id:someId})[0];
}
And then your html:
<div ng-repeat="subCategory in subCategorys | filter:{tags:tag}:true | orderBy:'id'">
{{subCategory.id}} {{subCategory.name}} {{subCategory.tags}}
<br/><br/>
You are now seeing details of {{ getSubCatById(2).name }}
</div>
I hope I interpreted your question correctly.

Sending different data to an embedded component in Ember

I've got a drag and drop setup in two views, currently. I am trying to pull those out and make reusable components out of them. At this point I'm not even worried about the actions and bringing that information back (that will be the next issue that I will look into). Right now I'm only worried about getting the proper data to display.
The templates for my current setup are:
The outside list view:
<ul class="sortable-list">
{{#each view.permission}}
{{ view App.GroupsPermissionView }}
{{/each}}
</ul>
The individual item view:
<li class="sortable-item">{{ this.displayName }}</li>
I have changed this into the following component templates:
drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{ drag-and-drop-item dadi=this.displayName }}
{{/each}}
</ul>
and drag-and-drop-item.handlebars:
<li class="sortable-item">{{ dadi }}</li>
I call this with {{ drag-and-drop dad=unselectedPermissions }} or {{ drag-and-drop dad=selectedPermissions }}
and this works fine.
Here is my issue. My other use of drag and drop does not use the field name of "displayName". The field is just "name". I'm sure other co-workers will need to also use other field names as well.
I've tried many different things to attempt to get it passed through to the inner component, but nothing has worked. I think it is a limitation in handlebars and that this will be impossible to do.
Any idea of what could be done here, if anything?
Edit: An update
I've got things somewhat closer...but now I've run into another related issue. Here's my current drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{ drag-and-drop-item self=this dadi=../field }}
{{/each}}
</ul>
Here's drag-and-drop-item.handlebars:
<li class="sortable-item">{{ dragAndDrop self dadi }}</li>
And here's drag_and_drop_helpers.js:
Ember.Handlebars.helper("dragAndDrop", function(context,value) {
return this.self.get(value);
});
I realize that context isn't being used above, I've got it in there because of testing I was doing.
Here's the thing...with the code above, I get the correct 'this'. If I return "this.self.get('displayName');" in the helper, it works. However, the ../field isn't being returned in the template. I so wish you could have nested {{}}'s...then this would work!
To get the ../field working, I found I could make the following change to drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{#if ../field}}
{{ drag-and-drop-item self=this dadi=../field }}
{{/if}}
{{/each}}
</ul>
But if I do this, 'this' isn't the right one, so I'm getting 'displayName' through, but the wrong context.
It seems I can have one, or the other, but not both.
I could really use some help here.
I got it figured out, after quite a few days of searching and trying lots of things.
I did not realize that you could use "view." to pull the information.
Here's the call to this:
{{ drag-and-drop dad=unselectedPermissions field="displayName" }}
Here's the working drag-and-drop.handlebars:
<ul class="sortable-list">
{{#each dad}}
{{ drag-and-drop-item self=this dadi=view.field }}
{{/each}}
</ul>
Here's the working drag-and-drop-item.handlebars:
<li class="sortable-item">{{ dragAndDrop dadi }}</li>
Here's the working drag_and_drop_helpers.js:
Ember.Handlebars.helper("dragAndDrop", function(value) {
return this.self.get(value);
});
I'm hoping this helps someone in the future so they don't repeat mistakes I've made.
Now, on to figuring out the coding for the component.js...

Categories

Resources