Linking user's contacts in firebase with md-contacts-chips - angularjs

I am having difficulty getting my head around on how I could link my users contacts in Firebase with md-contacts-chips from https://material.angularjs.org/0.11.2/#/demo/material.components.chips
Basically, each registered user can add people they know via email to their contacts list. The users firebase structure is as follows:
firebase
-$uid1
contacts
$uid2 - userObject
$uid3 - userObject
-$uid2
contacts
$uid1 - userObject
$uid3 - userObject
-$uid3
contacts
$uid1 - userObject
$uid2 - userObject
etc..
Is it possible to ng-repeat a users contacts as an array of objects?
How should I configure the md-contacts-chip?
The example has a function called loadContacts() which has the contacts set.
How would I be able to set my user objects as contacts? The return object is contact and I would like to find a way for it to return the queried object.
function loadContacts() {
var contacts = [
'Marina Augustine',
'Oddr Sarno',
'Nick Giannopoulos',
'Narayana Garner',
'Anita Gros',
'Megan Smith',
'Tsvetko Metzger',
'Hector Simek',
'Some-guy withalongalastaname'
];
return contacts.map(function (c, index) {
var cParts = c.split(' ');
var contact = {
name: c,
email: cParts[0][0].toLowerCase() + '.' + cParts[1].toLowerCase() + '#example.com',
image: 'http://lorempixel.com/50/50/people?' + index
};
contact._lowername = contact.name.toLowerCase();
return contact;
});
}
Thanks

I'm by no means an expert, just a trial and error fanatic. That being said I did get this to work. I the issues is that "md-contact-chip" uses "push and splice" to adjust the array, and as firebase states that's a bad idea. If we had access to replace push with $add() or splice with $remove() this should work properly.
I was looking at the custom chips setup and it seems possible because you can call a function during the chip's add and remove, then with a custom chip template could maybe get the same look as the contact-chips.
Anyway here is what I did to get it working with md-contact-chips. Also I've adjusted this one to work with a list of items, not contacts, cause I wanted the picture for the items.
The key to it should be get your whole person obj, then set the ng-model="ctrl.person.contacts" inside the controller make sure to have an array created if person.contacts does not exist. "ctrl.person.contacts = ctrl.person.contacts || [];
Yes your are not properly updating the firebase object but you when you run
ctrl.person.$save() you are just completely updating the db.
Html
<div layout="column" ng-cloak>
<div>
<p>Items selected</p>
<pre>{{ctrl.item.installedItems}}</pre>
</div>
<input type="button" ng-click="ctrl.updateInstalledItems()" value='update'>
<md-content class="md-padding autocomplete" layout="column">
<md-contact-chips
ng-model="ctrl.item.installedItems"
md-contacts="ctrl.querySearch($query)"
md-contact-name="alseSn"
md-contact-image="image"
md-require-match="true"
md-highlight-flags="i"
filter-selected="ctrl.filterSelected"
placeholder="Select installed items">
</md-contact-chips>
</md-content>
</div>
Controller
app.controller('ItemChipCtrl', ['items', 'item', '$q', '$log',
function (items, item, $q, $log) {
var ctrl = this;
ctrl.items = items;
ctrl.item = item;
ctrl.item.installedItems = ctrl.item.installedItems || [];
ctrl.querySearch = querySearch;
ctrl.allItems = loadItems(ctrl.items);
ctrl.filterSelected = true;
ctrl.updateInstalledItems = function() {
$log.info('Update....')
$log.log(ctrl.installedItems);
ctrl.item.$save();
}
/**
* Search for contacts.
*/
function querySearch (query) {
var results = query ?
ctrl.allItems.filter(createFilterFor(query)) : [];
return results;
}
/**
* Create filter function for a query string
*/
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(item) {
return (item.alseSn.indexOf(lowercaseQuery) != -1);
};
}
function loadItems(items) {
/*var items = $scope.items */
return items.map(function (c, index) {
var item = {
alseSn: c.alseSn || c,
alseCard: c.alseCard,
installedOn: c.installedOn || null,
image: 'img/items/47/'+c.alseCard+'.jpg' || null
};
return item;
});
}
}
]);
route injections
.when('/settings/:alseSn', {
templateUrl: 'settings.html',
controller: 'ItemChipCtrl as ctrl',
resolve: {
auth: function($location, Auth){
return Auth.$requireAuth().then(function(auth) {
return auth;
},function(error){
$location.path('/login');
});
},
item: function($route, Items, Auth){
return Auth.$requireAuth().then(function(){
return Items.getItem($route.current.params.alseSn).$loaded();
});
},
items: function(Items, Auth){
return Auth.$requireAuth().then(function(){
return Items.all.$loaded();
});
}
}
})

Related

how to connect 2 objects in ionic via backand

i need a bit of help to my ionic project. so i want to connect 2 objects in ionic, so that i can display the value of and object that has data from the other object.
<ion-list class="list-inset">
<ion-item class="item-text-wrap" ng-repeat='item in todos| filter:firma'>
<h2>{{item.company_name}}</h2>
</ion-item>
<ion-item class="item-text-wrap" ng-repeat='item in todos2 | filter:{ idcompany:com_id } '>
<h2>ponedeljek {{item.time_mon_start}}-{{item.time_mon_end}}</h2>
<h2>torek {{item.time_tue_start}}-{{item.time_tue_end}}</h2>
<h2>sreda {{item.time_wed_start}}-{{item.time_wed_end}}</h2>
<h2>Ĩetrtek {{item.time_thu_start}}-{{item.time_thu_end}}</h2>
<h2>petek {{item.time_fri_start}}-{{item.time_fri_end}}</h2>
<h2>sobota {{item.time_sat_start}}-{{item.time_sat_end}}</h2>
<h2>nedelja {{item.time_sun_start}}-{{item.time_sun_end}}</h2>
</ion-item>
this is what i want to display for the current company that i click on, but it shows all the times not only one that i want.
.controller('AppCtrl', function($scope, TodoService, cas, $state) {
$scope.todos = [];
$scope.todos2 = [];
function getAllTodos() {
TodoService.getTodos().then(function (result) {
$scope.todos = result.data.data;
$scope.firma = $state.params.aid;
});
}
getAllTodos();
function getalltimes() {
cas.getcompany().then(function (result) {
$scope.todos2 = result.data.data;
});
}
getalltimes();
})
.service('TodoService', function ($http, Backand, $state) {
var baseUrl = '/1/objects/';
var objectName = 'companies/';
function getUrl() {
return Backand.getApiUrl() + baseUrl + objectName;
}
function getUrlForId(id) {
return getUrl() + id;
}
getTodos = function () {
return $http.get(getUrl());
};
return {
getTodos: getTodos
}
})
.service('cas', function ($http, Backand, $state) {
var baseUrl = '/1/objects/';
var time = 'companies_timetable/';
function getUrl2() {
return Backand.getApiUrl() + baseUrl + time;
}
getcompany = function () {
return $http.get(getUrl2());
};
return {
getcompany: getcompany
}
})
this is now my app.js file and my connection to the backand service. this is working fine.
this are the companys from object 1
and this are the times showing
so when you click on the companys it should show the time for it. but here it shows all times that are in the object. i should show only one for the one company
i was trying to give all objects in one array (todos). but didn't worked rlly well. so please i need some help. if you need some more code or somthing just say it :).
Your second filter is wrong.
Simplest way to fix it is to call a function in $scope.
Change your filter like this:
<ion-item class="item-text-wrap" ng-repeat='item in todos2 | filter: isCurrentFirma '>
Then add a function in your controller:
$scope.isCurrentFirma = function(item){
// here check if item is equal to firma
// code like this:
return item.idcompany === $scope.firma.com_id
}

Angular material md-autocomplete sort and minimum length range to search

Since Im getting the data from my server.. i dont wana hit server from the very first letter to search...Im wana fix some min length after that md-autocompleate action should happn with proper sortings and with min 10 records as options...
currently i have this in my view page
<div ng-controller="ContactChipDemoCtrl as ctrl" layout="column" class="chipsdemoContactChips">
<md-content class="md-padding autocomplete" layout="column">
<md-contact-chips
ng-model="ctrl.contacts"
md-contacts="ctrl.querySearch($query)"
md-contact-name="name"
md-contact-image="image"
md-contact-email="email"
md-require-match=""
filter-selected="ctrl.filterSelected"
placeholder="To">
</md-contact-chips><!--md-min-length="3" i wonder y this for-->
<div ng-repeat="c in ctrl.contacts">{{c.name}}</div>
</md-content>
</div>
This in my controller and search query is email-Id
keynotesApp.controller('ContactChipDemoCtrl', DemoCtrl);
function DemoCtrl ($timeout, $q, $http) {
var self = this;
self.querySearch = querySearch;
self.contacts = [];
self.filterSelected = true;
/**
* Search for contacts.
*/
function querySearch (query) {
var result;
if(query) {
result = loadAndParseContacts().then(function(data) {
console.log(data)
return data.filter(createFilterFor(query))
})
} else {
result = []
}
return result
}
/**
* Create filter function for a query string
*/
function createFilterFor(query) {
var lowercaseQuery = angular.lowercase(query);
return function filterFn(contact) {
return (contact._lowername.indexOf(lowercaseQuery) != -1);;
};
}
function parse (data) {
return data.data.map(function (c, index) {
var contact = {
name: c.email,
};
contact._lowername = contact.name.toLowerCase();
return contact;
})
}
function loadAndParseContacts() {
var maxRecs = 10;
var fields = ('email _id name avatar organization jobTitle');
var sort = ({email:'ascending'});//this is not working i wonder why
return $http({
method: 'GET',
url: '/users/find',
params: {fields: fields, maxRecs: maxRecs, sort: sort }
})
.then(parse)
}
}
md-autocompleate is working fine ... bt sortings, min length,max records is all wat i want...do help thanks in advance

Map AngularJS restAPI request via URL

Being new to angular I'm stocked to figure out how to call a web service which should be parsed and maped via URL, like if the URL is getting called directly to get listed request with the given params
what I mean let say I have
/api/products -- calling all products(this is the access point)
/api/products/?page=2&orderby=asc -- calls products with pagination and orderby and here is what's bothering me because the api is getting called via ajax and there is no URL mapping of the target
My Codes
Html markup
<div ng-controller="MainCtrl">
<div class="row">
<div class="col-lg-7 col-md-7 col-sm-7">
<pagination total-items="totalItems" num-pages="totalPages" ng-model="currentPage" ng-change="selectPage(currentPage)" max-size="5" class="pagination-sm" boundary-links="true"></pagination>
</div>
<div ng-repeat="product in products"></div>
</div>
</div>
contoller
//called when navigate to another page in the pagination
$scope.selectPage = function(page) {
$scope.filterCriteria.pageNumber = page;
$scope.fetchResult();
};
//The function that is responsible of fetching the result from the server and setting the grid to the new result
$scope.fetchResult = function() {
return api.items.search($scope.filterCriteria).then(function(data) {
$scope.products = data;
$scope.totalPages = data.total;
$scope.productsCount = data.TotalItems;
}, function() {
$scope.products = [];
$scope.totalPages = 0;
$scope.productsCount = 0;
});
};
Service
.factory('api', function(Restangular) {
//api call to
return {
products: function() {
return Restangular.all('products').getList();
},
product: function(id) {
Restangular.one("products", id ).get().then(function(c) {
return c;
});
},
update: function(id) {
Restangular.one("products", id).put().then(function(c) {
return c;
});
},
items: {
search: function(query) {
return Restangular.all('products').getList(query);
}
},
};
});
How do I create params URL and function of this make restAPI calls or what are the workarounds in this case
You can get at the search parameters with the $location service. So if a user goes to somedomain.com/products/?page=2&orderby=asc then $location.search() will equal {page: 2, orderby: 'asc'}.
So when your controller loads you just need to set the filterCriteria and fetch the results.
controller
var myCtrl = function($location, $scope) {
$scope.selectPage = function(page) {
...
};
$scope.fetchResult = function() {
...
};
$scope.filterCriteria = $location.search();
$scope.fetchResults();
}
First you need to create a restangular object by mentioning the base url. In your case it will be
// It will creat a url /api
var base = Restangular.all('api');
Now you can create multiple scenarios like if you want to get all products then it will be:
// /api/products/
base.getList('products')
.then(function(products) {
$scope.products= products
})
Now if you want to apply pagination as well as include orderBy param
$scope.products = base.getList("products", [{page: 2},{orderby:asc}]);

AngularJS - RestAngular - after post add new category, update category list in other controller

what is the best way to update a list of categories (in a nav for example) after adding a category with a different controller?
Here is my code
// add new category
app.controller('AddCategoryController', ['$scope', 'CategoryService', function($scope, CategoryService) {
$scope.category = {};
$scope.added = false;
$scope.addCategory = function() {
CategoryService.addCategory($scope.category).then(function(response) {
if(response == 'ok') {
$scope.added = true;
}
});
};
}]);
and here is the controller for showing the categories
app.controller('CategoriesController', ['$scope', 'CategoryService', function($scope, CategoryService) {
CategoryService.getCategories().then(function(categories) {
$scope.categories = categories;
});
}]);
Categories are shown in a nav
<nav>
<div class="list-group" ng-controller="CategoriesController">
<a ng-href="#category/{{category.id}}" class="list-group-item" ng-repeat="category in categories" ng-bind="category.name"></a>
</div>
<div class="list-group">
Add category
</div>
</nav>
EDIT This is the service
services.factory('CategoryService', ['$route', 'Restangular', function($route, Restangular) {
var Category = Restangular.all('categories');
return {
getCategories: function() {
return Category.getList();
},
getCategory: function(id) {
id = id ? id : $route.current.params.categoryId;
return Category.get(id);
},
addCategory: function(category) {
return Category.post(category);
},
editCategory: function(category) {
return category.put()
},
removeCategory: function(id) {
id = id ? id : $route.current.params.categoryId;
return Category.remove(id);
}
};
}]);
Services are singleton in AngularJS. Therefore, after you called CategoryService.addCategory you can update the category list in your service and it will be available for other controllers.
You can also enrich your service to cache the categories. This will help you to avoid unnecessary requests to your backend.
Either you build your own caching logic or use:
RestangularProvider.setDefaultHttpFields({cache: true});
In addition you can use $rootScope.$on and $rootScope.$emit to receive and send events. This helps you to communicate between components in real-time fashion.
// send event
$rootScope.$emit(nameOfEvent, args...);
In some other controller/ service
// subscription
var unbind = $rootScope.$on(nameOfEvent, function(event, args...) { /* do stuff */ });
// don't forget to unbind
$scope.$on('$destroy', function() {
unbind();
});

using ng-include for a template that has directives that use data retrieved by XHR

I am using ng-include in order to include a persistent menu, that exists in all of the views of my SPA.
The problem is that I want to display different options and content in this menu per each user type(admin, guest, user etc.), and this requires the service function authService.loadCurrentUser to be resolved first.
For the purpose of managing this content easily and comfortably, I have created a simple directive, that takes an attribute with the required access level, and at the compile phase
of the element, if the permissions of the given user are not sufficient, removes the element and it's children.
So after failing miserably at trying to make the ng-include go through the routeProvider function, I've tried to use ng-init, but nothing seems to work, the user role remain undefined at the time that I am logging it out.
I am thinking about trying a new approach, and making the entire menu a directive that includes the template that is suitable for each user type, but first I would like to try and solve this matter.
Directive:
'use strict';
/* Directives */
angular.module('myApp.directives', []).
directive('restrict', function(authService){
return{
restrict: 'A',
prioriry: 100000,
scope: {
// : '#'
},
link: function(){
// alert('ergo sum!');
},
compile: function(element, attr, linker){
var user = authService.getUser();
if(user.role != attr.access){
console.log(attr.access);
console.log(user.role);//Always returns undefined!
element.children().remove();
element.remove();
}
}
}
});
Service:
'use strict';
/* Services */
angular.module('myApp.services', []).
factory('authService', function ($http, $q) {
var authServ = {};
var that = this;
that.currentUser = {};
authServ.authUser = function () {
return $http.head('/users/me', {
withCredentials: true
});
},
authServ.getUser = function () {
return that.currentUser;
},
authServ.setCompany = function (companyId) {
that.currentUser.company = companyId;
},
authServ.loadCurrentUser = function () {
var defer = $q.defer();
$http.get('/users/me', {
withCredentials: true
}).
success(function (data, status, headers, config) {
console.log(data);
that.currentUser.company = {};
that.currentUser.company.id = that.currentUser.company.id ? that.currentUser.company.id : data.main_company;
that.currentUser.companies = [];
for (var i in data.roles) {
that.currentUser.companies[data.roles[i]['company']] = data.roles[i]['company_name'];
if (data.roles[i]['company'] == that.currentUser.company.id){
that.currentUser.role = data.roles[i]['role_type'];
that.currentUser.company.name = data.roles[i]['company_name'];
// console.log(that.currentUser.role);
}
}
// defer.resolve(data);
defer.resolve();
}).
error(function (data, status, headers, config) {
that.currentUser.role = 'guest';
that.currentUser.company = 1;
defer.reject("reject");
});
return defer.promise;
}
return authServ;
});
Menu controller:
angular.module('myApp.controllers', []).
controller('menuCtrl', function($scope, $route, $location, authService){
//TODO: Check if this assignment should be local to each $scope func in order to be compliant with 2-way data binding
$scope.user = authService.getUser();
console.log($scope.user);
// $scope.companies = $scope.user.companies;
$scope.companyOpts = function(){
// var user = authService.getUser();
if(typeof $scope.user.company == 'undefined')
return;
var companies = [];
companies[$scope.user.company.id] = $scope.user.company.name;
for(var i in $scope.user.companies){
if(i != $scope.user.company.id){
companies[i] = $scope.user.companies[i];
}
}
// console.log(companies);
// if(nonCurrentComapnies.length > 0){
console.log(companies);
return companies;
// }
}
$scope.$watch('user.company.name', function(company){
for(var i in $scope.user.companies)
if(company == $scope.user.companies[i].id)
authService.setCompany(i);
});
$scope.$watch(function(){return authService.getUser().company; }, function(company){
//Refresh the page on company change here, first time, and each time the user changes the select
// $scope.companyOpts();
// $scope.currentComapany = company;
})
;})
Main SPA HTML page:
<div ng-init="authservice.loadCurrentUser" ng-include src="'partials/menu.html'"></div>
menu element that should be visible only to the admin:
<ul class="left" restrict access="admin">
<li>You are the admin!</li>
</ul>
Thanks in advance for any assistance!
I personally would do the "reverse" way. Which mean: I will add the menu in when the user role is "admin", or "user", etc...
This way, you can do something like this in the "restrict" directive:
...
var roleListener = $scope.$watch('user.role', function (newVal, oldVal) {
if (newVal == $scope.access) {
// add the menu items
// supposed that loadcurrentuser be called only once
// we should clear the watch
roleListener();
} else {
// personally, I would remove the item here too
// so the menu would be added or removed when user.role update
}
});
...
One more thing, for just display menu base on the user role, you can use ngSwitch, something like this:
<ul class="left" ng-switch="user.role">
<li ng-switch-when="admin">You are the admin!</li>
<li ng-switch-when="user">You are the user!</li>
<li ng-switch-default><img src="some-thing-running.gif"/>Your menu is loading, please wait...</li>
</ul>
And let the magical AngularJS binding render up the menus for you!
The call to authServ.getUser should also return a promise by calling internally
authServ.loadCurrentUser
which should be modified a bit to check if the user context exists to avoid making another API call and always returning resolve with the user context:
defer.resolve(that.currentUser);
Loading the user context should also be done early on as this enables the authorization of the app. The app.run function can be used for this purpose.
hope it helps others.

Resources