Angular 4 *ngFor, ngx-pipes get name of top-level key - javascript

I'm using the ngx-pipes in my ionic 3 app because I am pulling an object of objects from firebase.
I retrieve my objects and turn it into a variable that I can use on the page:
getEvents() {
this.firebaseDatabase.getEvents.subscribe(data => {
console.log("Events ", data);
this.events = data;
}, error => {
console.log("Events ", error);
});
}
I log the data and it comes back like this:
eventIDHashed_1:
endDateTime:"tomorrow"
location:"somewhere"
roles:
GyyapYhHQDOriruHLvGPKaTOiRp2:"admin"
startDateTime:"today"
title:"Arisss & Nathan"
type:"wedding"
eventIDHashed_2:
endDateTime:"tomorrow"
location:"somewhere"
roles:
GyyapYhHQDOriruHLvGPKaTOiRp2:"admin"
startDateTime:"today"
title:"Jack & Marlana"
type:"wedding"
The way I'm displaying it on the page is a temporary solution, but everything is working fine except the first line. I need to get the event name (the key) where it says eventIDHashed_1 and eventIDHashed_2
<div class="event-container">
<div *ngFor="let event of events | values; let i = index">
<span>{{events[i]}}</span>
<span>{{event.title}}</span>
<span>{{event.location}}</span>
<span>{{event.type}}</span>
<span *ngFor="let role of event.roles | pairs">
<span>{{role[0]}}, {{role[1]}}</span>
</span>
<span>{{event.startDateTime}}</span>
<span>{{event.endDateTime}}</span>
</div>
</div>
I have everything working and am able to retrieve all information, except the actual event ID <span>{{events[i] | keys}}</span> This gives me a list of all of the keys inside of it (endDateTime, location, title, startDateTime...), but I need to get the id of the event eventIDHashed_1 and eventIDHashed_2

After playing with ngx-pipes, I found that I could use the pairs operator fairly simply. I've changed the template from the above to this:
<div *ngFor="let event of events | pairs">
<span>{{event[0]}}</span>
<span>{{event[1].title}}</span>
<span>{{event[1].location}}</span>
<span>{{event[1].type}}</span>
<span *ngFor="let role of event[1].roles | pairs">
<span>{{role[0]}}, {{role[1]}}</span>
</span>
<span>{{event[1].startDateTime}}</span>
<span>{{event[1].endDateTime}}</span>
</div>
ngx-pairs gives me the key (position [0]) and the value (position [1]) of the event

Related

Retrieve Firebase database specific value depending on key with angular 5

as the title says I build a CRUD application using Angular 5 and CRUD works just fine but now I need to retrieve a specific post data with advanced routing so the url should be something like :
https://example.com/blog/howtoDoSomething
and the page should contain this post details like name, content , image and so on .
I tried
details.component.ts
this.sub = this.route.params.subscribe(params => {
this.key = params['$key'];
});
details.component.html
<p>
this page about {{ $key }}
</p>
the above code worked fine with angular 4 with some difference of course and I used a json file not Firebase .
I also tried this one :
details.component.ts
var x = this.blogService.getData();
x.snapshotChanges().subscribe(item => {
this.blogList = [];
item.forEach(element => {
var y = element.payload.toJSON();
y["$key"] = element.key;
this.blogList.push(y as Blog);
});
});
details.component.html
<div *ngFor="let blog of blogList">
{{blog[$key].title}}
</div>
this one if I use blog alone like :
<div *ngFor="let blog of blogList">
{{blog}}
</div>
this retrieves the whole database as as
[object object] repeated
thanks in advance .
// user-service.ts
public getUser(userId: string) {
return this.afs.doc('users/' + userId).valueChanges();
)
// user-component.ts
// Get userId from route
this.user$ = this.userService.getUser(userId);
// user-component.html
<div *ngIf="user$ | async as user">
{{user | json}}
</div>

Vue.js: How to map a list of keys to Firebase objects?

I develop a small web-app based on Vue.js using Firebase to store and sync the data. I store items (e.g. with attributes title and subtitle) and lists with an attribute listitems, where an array of keys (those generated from Firebase) of items is stored. The structure looks like this:
Now the problem: I want to display a list and show the items from the listitems attribute and I'm doing it like this:
Compontent:
var ShowList = Vue.extend({
template: '#show-list',
firebase: {
// get all existing items from firebase
items: firebase.database().ref('items')
},
data: function () {
// get list item keys of list 'list_id' and bind it to this.list
this.$bindAsObject('list', listsRef.child(this.$route.params.list_id));
return {
list: this.list
};
}
});
Template:
<!-- show a list -->
<template id="show-list">
<ul v-if="list.items != ''">
<li v-for="key in list.items"> <!-- I would like to not being forced to -->
<template v-for="item in items"> <!-- iterate the whole list of existing items -->
<span v-if="item['.key'] == key">
{{ item.title }}
</span>
</template>
</li>
</ul>
<div v-else>No items.</div>
</template>
As you can see, I have to use two iterations where I iterate the full items list for every entry in list.items.
My question: Is there a more efficient way to map the actual objects to the list of object keys? For a huge number of item records, my approach will be very slow. Maybe I'm just too blind to see a simpler solution?
Thanks for your time!
I think you have to denormalize/duplicate some data there. I had a similar situation and this Firebase video cleared a lot of things up for me: https://youtu.be/ran_Ylug7AE?t=2m22s (Link updated to passage at 2:22. The whole serie is worth watching btw.)
My shot at it would be adding (Firebase) keys in "listitems", just like you have done in "items", with only the most crucial data there, so that you can link to a full description
Is your data read only? In which case you could move the filter logic from your template to your data module, like so (I expect I have unintended side-effects):
data: function () {
// get list item keys of list 'list_id' and bind it to this.list
this.$bindAsObject('list', listsRef.child(this.$route.params.list_id));
var items = firebase.database().ref('items')
var activeItems = this.list.items.map(function(key) {
return items[key]
})
return {
activeItems: activeItems;
};
}

Using splice on dynamic array in Angular repeater

I have an issue when trying to delete an object from an array using splice. I have an array that is dynamically created through a UI and stored in a scope variable called $scope.productAttributes.Products. This is an example of what it looks like...
[
{
"ProductLabel":"Net",
"Code":"ela",
"Site":"SITE1"
},
{
"ProductLabel":"Link",
"Code":"eli",
"Site":"SITE1"
},
{
"ProductLabel":"24-port managed PoE switch",
"Code":"24p",
"Site":"SITE2"
},
{
"ProductLabel":"Dedicated Firewall",
"Code":"ded",
"Site":"SITE2"
},
{
"ProductLabel":"Link",
"Code":"eli",
"Site":"SITE3"
},
{
"ProductLabel":"IPv4 Addresses",
"Code":"ip4",
"Site":"SITE3"
}
]
I then display that array in an angular repeater and group it by 'site' (which might be part of the problem)...
<div ng-repeat="(key, value) in productAttributes.Products | groupBy: 'Site'">
<strong>{{key}}</strong>
<div ng-repeat="site in value">
<h4>{{site.ProductLabel}}</h4>
<sapn href="" ng-click="deleteItem($index)" class="text-danger">Remove {{site.ProductLabel}}</span>
</div>
</div>
</div>
On the delete button I pass in the index of the object and use the splice function...
$scope.deleteItem = function (index) {
$scope.productAttributes.Products.splice(index, 1);
};
So the issue is that the $index is always zero (I noticed this from a console.log) as I mentioned that I think it might be down to the groupBy but I am not sure. anyone know whats going wrong? Thanks
UPDATE:
It would seem the problem is with the $index in the nested repeater. So if the json above the structure would be...
SITE1:
Product: Net - $index: 0
Product: Link - $index: 1
SITE2:
Product: 24-port - $index: 0
Product: Dedicated - $index: 1
SITE3:
Product: Link - $index: 0
Product: IPV4 - $index: 1
So if I try to delete the IPV4 product in SITE3, it removes the LINK product in Site1 as it has the same $index. any ideas how I can fix that?
We can not rely on $index as it does not contain the updated value after you remove an item from array.
Pass the object dynamically from UI and delete it from model using below code:
In Html:
<div ng-repeat="(key, value) in productAttributes.Products | groupBy: 'Site'">
<strong>{{key}}</strong>
<div ng-repeat="site in value">
<h4>{{site.ProductLabel}}</h4>
<sapn href="" ng-click="deleteItem(site)" class="text-danger">Remove {{site.ProductLabel}}</span>
</div>
</div>
</div>
In JavaScript:
$scope.productAttributes.Products.splice
($scope.productAttributes.Products.indexOf(site), 1);
This causes model to update with updates values in repeater and re-renders it on UI.
OK - I ended up doing it this way and it seems to work
$scope.deleteItem = function (item) {
var index = $scope.productAttributes.Products.indexOf(item);
$scope.productAttributes.Products.splice(index, 1);
};
So passing in the whole object seems to have worked. I'm not sure why.

Filter variable not updating in controller angularjs

Hi Im attempting to build functionality around the length of a filter in angularjs, and although its working as it should in the view, in the controller the variable seems to stay outdated...
When I click on the div below it filters a list and calls the filterby function. The output of the length of the newly filtered list updates in the view correctly. However in the function itself I have a log set and it is still showing the old length when I click on the div.
<div ng-repeat="filter in filters" ng-click="filterby(filter.filter_type)">{{filter.filter_type}}</div>
<ul>
<li ng-repeat="event in filtered = (events | filter:query) | orderBy:'-event_date' ">
<span >{{event.event_date}},{{event.event_name}}, {{event.event_venue}}, {{event.event_description}} </span>
</li>
</ul>
<br />Length of filtered items {{filtered.length}}
And my view....
$scope.filterby = function(filterby) {
if (filterby == 'ALL') {
$scope.query = '';
}
else {
$scope.query = filterby;
}
console.log($scope.filtered.length);
};
My filter data:
$scope.filters = [
{'filter_type' : 'ALL'},
{'filter_type' : 'Macnass'}
];
EDIT: Ok its not that it nots working at all, its just showing the previous value, as if its one click behind all the time, so its something to do with the fact that the variable in the view is updated after the list is made. but Im not sure how to go about insuring the variable in the controller is the latest value.
Plunker: http://plnkr.co/edit/u7KpqYx8gDwaaXEvGeMn?p=preview
check out the plunker
added below
$scope.filtered = $filter('filter')($scope.events, $scope.query)
in $scope.filterby function

AngularUI-Bootstrap Typeahead: Grouping results

I am implementing typeahead using AngularUI-Bootstrap. I need to show the results grouped based on some values coming from the database. Here's a sample scenario
There are some users in the database, each user has a "Department". One user name can be available in multiple departments.
The end-user types in the names to search users from the database and retrieves the list in the typeahead list. Since one user name can belong to multiple departments, the requirement is to show the user names grouped by different departments. Something like this:
Then the user can select the desired user name and proceed.
As per the Typeahead documentation present here, I don't see any option to cater to my requirement.
I have tried the this workaround: Whenever the typeahead array is getting formed, I appended the user department to the array element:
$scope.fetchUsers = function(val) {
console.log("Entered fetchUsers function");
return $http.get("http://localhost:8080/TestWeb/users", {
params : {
username : val
}
}).then(function(res) {
console.log("Response:",res);
var users = [];
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
console.log("users=",users);
return users;
});
};
This way, at least the end user sees the department. But when I select the record, the selected value is the full content of the array element. Below is sample screenshot to elaborate:
HTML
Users from local service
<pre>Model: {{userList | json}}</pre>
<input type="text" ng-model="userList" placeholder="Users loaded from local database"
typeahead="username for username in fetchUsers($viewValue)"
typeahead-loading="loadingUsers" class="form-control">
<i ng-show="loadingUsers" class="glyphicon glyphicon-refresh"></i>
User types in the string
User selects one record
I want to avoid the department (in this case, string - Desc 4 ) when user selects a record.
Is there any way I can achieve this grouping without any workaround? Or is there any way I can enhance my workaround?
I used to have a similar requirement and here is how I did it that time.
Example Plunker: http://plnkr.co/edit/zujdouvB4bz7tFX8HaNu?p=preview
The trick is to set the typeahead-template-url to a custom item template:
<input type="text" class="form-control" placeholder="Users loaded from local database"
ng-model="selectedUser"
typeahead="user as user.name for user in getUsers($viewValue)"
typeahead-template-url="typeahead-item.html" />
The item template, this represent each item in a dropdown:
<div class="typeahead-group-header" ng-if="match.model.firstInGroup">Desc {{match.model.group}}</div>
<a>
<span ng-bind-html="match.label | typeaheadHighlight:query"></span>
</a>
As you can see, there is an ng-if to show a group header if that item has a property firstInGroup set to true.
The firstInGroup properties are populated like this using lodashjs:
$scope.getUsers = function (search) {
var filtered = filterFilter(users, search);
var results = _(filtered)
.groupBy('group')
.map(function (g) {
g[0].firstInGroup = true; // the first item in each group
return g;
})
.flatten()
.value();
return results;
}
Hope this fit to your requirement too.
please see here http://plnkr.co/edit/DmoEWzAUHGEXuHILLPBp?p=preview
instead of creating new objects here:
angular.forEach(res.data, function(item) {
users.push(item.UserName + " - " + item.UserDepartment);
});
use create template :
<script type="text/ng-template" id="customTemplate.html">
<a> {{ match.model.name}} - department : {{match.model.dept}}</a>
</script>
and use it in your Typeahead directive
<input type="text" ng-model="selected"
typeahead="user.name as user for user in users | filter:$viewValue | limitTo:8" class="form-control"
typeahead-template-url="customTemplate.html">

Categories

Resources