Firebase child_added and ng-repeat - angularjs

I used child_added and ng-repeat to display a list of statuses, this part worked fine, but when I click on one of the statuses in the list i am sent to tab/statuses/ instead of going to tab/statuses/:statusid. Any help would be much appreciated.
app.js
.state('tab.statuses', {
url: "/statuses",
views: {
'tab-statuses': {
templateUrl: "templates/statuses.html",
controller: "StatusesController"
}
}
})
.state('tab.status', {
url: "/statuses/:statusid",
views: {
'tab-statuses': {
templateUrl: "templates/status.html",
controller: 'StatusController'
}
}
})
statusescontroller
app.controller('StatusesController', function ($scope, $filter, $state, $ionicListDelegate, $ionicActionSheet, StatusesService) {
$scope.statuses = [];
var ref = new Firebase("https://app85.firebaseio.com/statuses");
ref.on("child_added", function(snapshot, prevChildKey) {
var status = snapshot.val();
$scope.statuses.push(status);
});
})
statuses.html
<ion-item ng-repeat="status in statuses" href="#/tab/statuses/{{status.$id}}" class="customItemSize mlSmallerFont item-icon-left item-icon-right">
<i class="icon ion-ios-person-outline"></i>
{{ status.update }}
</ion-item>

You seem to be borrowing some ideas from AngularFire, but don't have the code to implement them. As it currently is the {{status.$id}} in your HTML has no value.
To fix that you should either use AngularFire or populate a $id in your controller:
ref.on("child_added", function(snapshot, prevChildKey) {
var status = snapshot.val();
status['$id'] = snapshot.key();
$scope.statuses.push(status);
});
Note that this will only work when snapshot.val() is an object (so not when it's a primitive). But since that is the case in your snippet, this should work.

Related

Restangular / Json-ld | I can get the data, but I can't show them

I'm new in Angular and need your help.
With my team, we made a PHP REST API in Symfony that we want to connect to an Angular client.
I already have the Angular app..
I can see in my XHR requests that i actually get the entities and their properties (users).
But in my view, i tried some {{ user.name }} with ng-repeat but it dosen't show anything.
Datas are sent in Json-ld and we use Restangular to read them.
Here some code :
app.js :
angular
.module('frontApp', [
'ngAnimate',
'ngCookies',
'ngResource',
'ngRoute',
'ngSanitize',
'ngTouch',
'restangular'
])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.when('/about', {
templateUrl: 'views/about.html',
controller: 'AboutCtrl'
})
.otherwise({
redirectTo: '/'
});
})
.config(['RestangularProvider', function (RestangularProvider) {
// The URL of the API endpoint
RestangularProvider.setBaseUrl('http://localhost:8000');
// JSON-LD #id support
RestangularProvider.setRestangularFields({
id: '#id'
});
RestangularProvider.setSelfLinkAbsoluteUrl(false);
RestangularProvider.setResponseExtractor(function(response) {
var newResponse = response;
if (angular.isArray(response)) {
angular.forEach(newResponse, function(value, key) {
newResponse[key].originalElement = angular.copy(value);
});
} else {
newResponse.originalElement = angular.copy(response);
}
return newResponse;
});
// Hydra collections support
RestangularProvider.addResponseInterceptor(function (data, operation) {
// Remove trailing slash to make Restangular working
function populateHref(data) {
if (data['#id']) {
data.href = data['#id'].substring(1);
}
}
// Populate href property for the collection
populateHref(data);
if ('getList' === operation) {
var collectionResponse = data['hydra:member'];
collectionResponse.metadata = {};
// Put metadata in a property of the collection
angular.forEach(data, function (value, key) {
if ('hydra:member' !== key) {
collectionResponse.metadata[key] = value;
}
});
// Populate href property for all elements of the collection
angular.forEach(collectionResponse, function (value) {
populateHref(value);
});
return collectionResponse;
}
return data;
});
}])
;
main.js :
angular.module('frontApp')
.controller('MainCtrl', function ($scope, Restangular) {
$scope.ami1 = Restangular.one('amis',1).get();
$scope.test ='Test';
var amis = Restangular.all('amis');
amis.getList().then(function (ami) {
$scope.ami = ami;
});
});
main.html:
<div class="jumbotron">
<p>Liste d'amis :</p>
{{ ami1.nom }}
{{ test }}
<div ng-repeat="ami in amis" id="{{ ami['#id'] }}" class="row marketing">
<h1>{{ ami.nom }}</h1>
<h2>{{ ami.prenom }}</h2>
<h3>{{ ami.id }}</h3>
</div>
chrome console
Thanks for helping!
I found by myself, here is the code :
<div ng-repeat="ami in amis" id="{{ ami['#id'] }}" class="row marketing">
<h1>Nom: {{ ami.nom }}</h1>
<h2>Prenom: {{ ami.prenom }}</h2>
<h3>Date de naissance: {{ ami.dateNaissance }}</h3>
<h4>Telephone(s): </h4>
<div ng-repeat="telephone in ami.telephones" id="{{ telephone['#id'] }}">
{{ telephones[$index].numero }}
</div>
</div>
in your controller ...
$scope.amis = Restangular.all('amis').getList();
if that doesn't work ...
Restangular.all('amis').getList().then(function(response) {
$scope.amis=response;
}).catch(function(error) {
console.log(error);
})
Ok now i have another issue.
I have all of my 'ami' and their properties so it's really great, but my 'ami' have many 'telephone' ids in it.
What i try to do is to have my 'ami' 'numero' from 'telephone' with this telephone id.
i can list all of my 'amis' with telephon id in it, and all of my 'telephones' with the numeros in it.
Thanks angain :)

angularjs state parameters not working

I'm having an issue trying to pass a parameter object to a state using stage.go().
Here is my state definition:
.state('drillhole.ddhinttype', {
url: '/ddhinttype',
templateUrl: VIRTUAL_DIR_PATH + '/App/Views/drillholemanager/drillhole/tabddhinttype.html?v=' + fileVer,
controller: 'DrillHoleDdhIntTypeController',
params: { name: null, description: null }
})
And here is my controller:
try {
angular.module('centric.drillhole.manager');
} catch (e) {
angular.module('centric.drillhole.manager', ['app.config', 'ui.router', 'kendo.directives', 'ui.bootstrap', 'ngCookies', 'centric.common', 'centric.notification', 'pascalprecht.translate', 'centric.security', 'centric.app.settings']);
}
angular.module('centric.drillhole.manager').controller('DrillHoleDdhIntTypeController', ['$scope', 'CentricUIHelper', 'NumberHelper', 'DrillHoleManagerService', 'app.config', '$stateParams',
function ($scope, uihelper, numberHelper, service, appconfig, $stateParams) {
$scope.loading = false;
$scope.isbusy = function () {
return $scope.loading || $scope.$parent.loading;
}
var load = function () {
var hello = $stateParams.name;
var hello2 = $stateParams.description;
};
load();
}]);
And I'm calling the state like so:
$state.go('drillhole.ddhinttype', { name: tab.params.name, description: tab.params.description });
In my controller the name and description properties are always null.
Not sure what I'm missing here. Any ideas?
If you put the params in your url you will be able to access it in controller using $stateParams
.state('drillhole.ddhinttype', {
url: '/ddhinttype/:name/:description',
templateUrl: VIRTUAL_DIR_PATH + '/App/Views/drillholemanager/drillhole/tabddhinttype.html?v=' + fileVer,
controller: 'DrillHoleDdhIntTypeController',
params: { name: null, description: null }
})
You can read more about url routing here: https://github.com/angular-ui/ui-router/wiki/url-routing
Try this in the state definition:
params: { name: undefined, description: undefined }
or this:
params: ['name', 'description']
I feel like I should post the final result. I have decided to pass the parameter in the URL so that I can re-use the same controller for several tabs which each have the same functionality but against different tables in the DB.
Here is the part of my base controller which creates the tabs (CoreLogController.js):
service.getDrillHoleIntervalTypes()
.success(function (res) {
$scope.data.drillHoleIntervalTypes = res;
for (var i = 0; i < $scope.data.drillHoleIntervalTypes.length; i++) {
// add the tab and set it as active if we're in the correct $state
$scope.dynamictabs.push({ heading: $scope.data.drillHoleIntervalTypes[i].Name, route: 'drillhole.ddhinttype', params: { ddhinttype: $scope.data.drillHoleIntervalTypes[i].Name }, active: ($scope.$state.params.ddhinttype == $scope.data.drillHoleIntervalTypes[i].Name) });
}
})
.error(function (error) {
uihelper.showError(error);
});
And here is the relevant HTML portion where the tabs are shown (corelog.html):
<tabset>
<tab ng-repeat="t in statictabs" heading="{{t.heading}}" ui-sref="{{t.route}}" active="t.active"></tab>
<tab ng-repeat="t in dynamictabs" heading="{{t.heading}}" ui-sref="drillhole.ddhinttype({ddhinttype: '{{t.params.ddhinttype}}'})" active="t.active"></tab>
</tabset>
And here is where I define the state (app.js):
.state('drillhole.ddhinttype', {
url: '/ddhinttype/{ddhinttype}',
templateUrl: VIRTUAL_DIR_PATH + '/App/Views/drillholemanager/drillhole/tabddhinttype.html?v=' + fileVer,
controller: 'DrillHoleDdhIntTypeController',
params: { ddhinttype: null }
})
I now get access to the ddhinttype variable on each instance of the controller (DrillHoleDdhIntTypeController.js) which tells it which table to perform operations against.
Since ddhinttype is also contained the URL the user can create a bookmark which will bring them right back to the same tab even though they are dynamically generated.

Angularjs - read inside json file

This is my first attempt with angularjs and ionic-framework.
I have an example json file and i'd like to display onscreen some data from it.
The displaying-data bit works, but i'd like to populate a "details" page with some info that are stored as an abject inside the main json file, and i need to use the id from the url to select to display only the data that i need.
Here's some code:
App.js
angular.module('hgapp', ['ionic', 'hgapp.controllers', 'ngResource'])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/menu.html',
controller: 'AppCtrl'
})
.state('app.details', {
url: '/details/:roomID',
views: {
'menuContent': {
templateUrl: 'templates/details.html',
controller: 'DetailsCtrl'
}
}
})
$urlRouterProvider.otherwise('/app/home');
});
Controllers.js
angular.module('hgapp.controllers', ['hgapp.services'])
.controller('AppCtrl', function ($scope, HGJson) {
HGJson.get(function (data) {
$scope.rooms = data.data;
})
})
.controller('DetailsCtrl', function ($scope, $stateParams, HGJson) {
$scope.roomID = $stateParams.roomID;
console.log($stateParams.roomID);
})
services.js
angular.module('hgapp.services', ['ngResource'])
.factory('HGJson', function ($resource) {
return $resource('json/data.json')
});
Data.json (Just a simplified example)
{
tm: 00000000,
errors: 0,
data: {
{id: 0, name: Value 0, url:url-0},
{id: 1, name: Value 1, url:url-1},
{id: 2, name: Value 2, url:url-2}
}
details.html
<ion-view view-title="Details">
<ion-content>
<h1>{{roomID}}</h1>
</ion-content>
In the details page i'm printing the roomID just to see if the controller (detailsCtrl) works, and i have the correct id printed every time. Now, the bit where i'm stuck is how to manipulate the data from HGJson service so that it allows my to print on data from the right room id.
I hope this question is clear enought, if not, feel free to ask for more clarification.
Thanks a lot
EDIT
At the end i solved it adding this to my controller.js file:
.controller('DetailsCtrl', function ($scope, $stateParams, HGJson) {
HGJson.get(function (data) {
angular.forEach(data.data, function (item) {
if (item.id == $stateParams.roomID)
$scope.currentRoom = item;
});
});
})
Just do the same thing as what you're doing in the app controller, but find the room you want in the returned JSON:
HGJson.get(function (data) {
$scope.room = data.data.filter(function(room) {
return room.id == $stateParams.roomID);
})[0];
});
You could also put that filtering functionality in your service, so that in the future, when you have a real dynamic backend, you call a different URL returning only the requested room rather than calling a URL that returns all the rooms.
angular.module('hgapp.services')
.factory('HGJson', function ($http) {
return {
getRooms: function() {
return $http.get('json/data.json').then(function(response) {
return response.data;
});
},
getRoom: function(roomId) {
return $http.get('json/data.json').then(function(response) {
return response.data.data.filter(function(room) {
return room.id == roomID;
})[0];
});
}
};
});
Note that your JSON is invalid: data must be an array, not an object.
In your controller, you will need to create a function to "find" the correct object in your data object.
Try something like this:
$scope.getRoom = function(id) {
for(var i in $scope.rooms) {
if($scope.rooms[i].id === id) {
return $scope.rooms[i];
}
}
};
And you can display it in your DOM:
{{ getRoom(roomID) }}
BUT it would probably be even better to set the current room to a scoped variable instead of running the function every time. So in this case (I strongly recommend), instead of returning $scope.rooms[i], you could set angular.copy($scope.rooms[i], $scope.currentRoom) (this will copy the room into the currentRoom scoped variable) and then use it in the DOM with simply {{ currentRoom }}
Good luck!

AngularJS and UI-Router: keep controller loaded

I am building a web application for our customer support. One of the needs is to be able to keep multiple tickets opened at the same time.
I was able to do the first part easily using a tabulation system and UI-Router.
However, with my current implementation, each time I change active tab, the previously-current tab is unloaded, and the now-current tab is loaded (because it was unloaded with a previous tab change).
This is not at all the expected behavior. I've already spent a couple of days trying to find a way to achieve this, without any luck.
The closest thing I was able to do is to use the multiple views system from UI-Router, but I need multiple instance of the same view to keep in memory (if multiple tickets are opened, they all are on the same view, with the same controller, but a different scope)
Here's my current implementation:
supportApp.js:
var app = angular.module("supportApp", ["ui.router", "ui.bootstrap"]);
app.config(function($stateProvider, $urlRouterProvider, $httpProvider){
$urlRouterProvider.otherwise("/");
$stateProvider
.decorator('d', function(state, parent){
state.templateUrl = generateTemplateUrl(state.self.templateUrl);
return state;
})
.state("main", {
abtract: true,
templateUrl: "main.html",
controller: "mainController"
})
.state("main.inbox", {
url: "/",
templateUrl: "inbox.html",
controller: "inboxController"
})
.state('main.viewTicket', {
url: '/ticket/{id:int}',
templateUrl: "viewTicket.html",
controller: "ticketController"
})
;
});
mainController.js: (handles other stuff, minimal code here)
app.controller("mainController", function($rootScope, $http, $scope, $state, $interval){
// Tabs system
$scope.tabs = [
{ heading: "Tickets", route:"main.inbox", active:false, params:{} }
];
var addTabDefault = {
heading: '',
route: null,
active: false,
params: null,
closeable: false
};
$rootScope.addTab = function(options){
if(!options.hasOwnProperty('route') || !options.route)
{
throw "Route is required";
}
var tabAlreadyAdded = false;
for(var i in $scope.tabs)
{
var tab = $scope.tabs[i];
if(tab.route == options.route && angular.equals(tab.params, options.params))
{
tabAlreadyAdded = true;
break;
}
}
if(!tabAlreadyAdded)
{
$scope.tabs.push($.extend({}, addTabDefault, options));
}
if(options.hasOwnProperty('active') && options.active === true)
{
$state.go(options.route, options.hasOwnProperty('params')?options.params:null);
}
};
$scope.removeTab = function($event, tab){
$event.preventDefault();
if($scope.active(tab.route, tab.params))
{
$scope.go($scope.tabs[0].route, $scope.tabs[0].params);
}
$scope.tabs.splice($scope.tabs.indexOf(tab), 1);
};
$scope.go = function(route, params){
$state.go(route, params);
};
$scope.active = function(route, params){
return $state.is(route, params);
};
$scope.$on("$stateChangeSuccess", function() {
$scope.tabs.forEach(function(tab) {
tab.active = $scope.active(tab.route, tab.params);
});
});
});
main.html:
<div class="container-fluid" id="sav-container">
<div class="row-fluid">
<div class="col-lg-2">
<form role="form" id="searchForm" action="#">
<div class="form-group has-feedback">
<input class="form-control" type="search" />
<span class="glyphicon glyphicon-search form-control-feedback"></span>
</div>
</form>
</div>
<div class="col-lg-10" id="support_main_menu">
<ul class="nav nav-tabs">
<li ng-repeat="t in tabs" ng-click="go(t.route, t.params)" ng-class="{active: t.active, closeable: t.closeable}" style="max-width: calc((100% - 128px) / {{tabs.length}});">
<a href class="nav-tab-text">
<button ng-show="t.closeable" ng-click="removeTab($event, t)" class="close" type="button">×</button>
<span>{{t.heading}}</span>
</a>
</li>
</ul>
</div>
</div>
<div class="row-fluid">
<div class="tab-content" ui-view></div>
</div>
</div>
It seems to me that what I ask is pretty standard, but I sadly couldn't find any usefull thing on the Internet
The basic idea is to store state (i.e. list of tickets) in a service as opposed to a controller. Services hang around for the life of the application. There are some articles on this. I'm still developing my approach but here is an example:
var RefereeRepository = function(resource)
{
this.resource = resource; // angular-resource
this.items = []; // cache of items i.e. tickets
this.findAll = function(reload)
{
if (!reload) return this.items;
return this.items = this.resource.findAll(); // Kicks off actual json request
};
this.findx = function(id)
{
return this.resource.find({ id: id }); // actual json query
};
this.find = function(id) // Uses local cache
{
var itemx = {};
// Needs refining
this.items.every(function(item) {
if (item.id !== id) return true;
itemx = item;
return false;
});
return itemx;
};
this.update = function(item)
{
return this.resource.update(item);
};
};
refereeComponent.factory('refereeRepository', ['$resource',
function($resource)
{
var resource =
$resource('/app_dev.php/referees/:id', { id: '#id' }, {
update: {method: 'PUT'},
findAll: {
method: 'GET' ,
isArray:true,
transformResponse: function(data)
{
var items = angular.fromJson(data);
var referees = [];
items.forEach(function(item) {
var referee = new Referee(item); // Convert json to my object
referees.push(referee);
});
return referees;
}
},
find: {
method: 'GET',
transformResponse: function(data)
{
var item = angular.fromJson(data);
return new Referee(item);
}
}
});
var refereeRepository = new RefereeRepository(resource);
// Load items when service is created
refereeRepository.findAll(true);
return refereeRepository;
}]);
So basically we made a refereeRepository service that queries the web server for a list of referees and then caches the result. The controller would then use the cache.
refereeComponent.controller('RefereeListController',
['$scope', 'refereeRepository',
function($scope, refereeRepository)
{
$scope.referees = refereeRepository.findAll();
}
]);

Angular Fire - Display Issue with Empty Data

I am new to Firebase and Angular JS so please excuse any obvious "what the heck are you doing's"...
I have a few pages that are nested:
A) Directory (templates/tab-directory.html)
A.1) Specialty (templates/tab-specialty.html)
A.1.1) Team (templates/tab-team.html)
My tab-specialty.html looks like this:
<ion-view view-title="{{specialty.name}}">
<ion-header-bar class="bar-positive bar-subheader item-input-inset">
<div class="button-bar">
...
</div>
</ion-header-bar>
<ion-content class="has-subheader">
...
</ion-content>
</ion-view>
This doesn't always display the data. I know I am doing something wrong with asynchronous calls or something along those lines because it seems like the data is just not 'ready' for me to display.
The {{specialty.name}} doesn't always display. If I do ionic serve on the specialty page, it will display fine. However, when I start from one level higher and click into that page, it will not.
My app.js has the following states in it:
.state('tab.directory', {
url: '/directory',
views: {
'tab-directory': {
templateUrl: 'templates/tab-directory.html',
controller: 'DirectoryCtrl'
}
}
})
.state('tab.specialty', {
url: '/specialty/:specialtyId',
views: {
'tab-directory': {
templateUrl: 'templates/tab-specialty.html',
controller: 'SpecialtyCtrl'
}
}
})
.state('tab.team', {
url: '/team/:teamId',
views: {
'tab-directory': {
templateUrl: 'templates/tab-team.html',
controller: 'TeamCtrl'
}
}
})
My controllers.js has the following controller for specialty:
...
.controller('SpecialtyCtrl', function($scope, $stateParams, Specialties, Staff, Teams){
var specialtyId = $stateParams.specialtyId;
...
Specialties.$loaded().then(function(array){
var syncArray = array.getById(specialtyId);
if(syncArray.length > 0){
$scope.specialty = syncArray[0];
console.log($scope.specialty);
}
});
Staff.$loaded().then(function(array){
var allArray = array.getBySpecialtyId(specialtyId);
hasConsults = getHasConsults(allArray);
if(hasConsults){
// Default to consults if we have them
$scope.isConsultsActive = true;
$scope.staffList = allArray.filter(function(staff){
return staff.is_consult;
});
} else {
// If no consults, default to all
$scope.isAllActive = true;
$scope.staffList = allArray;
}
});
// Watch for new/updated/removed staff for this specialty
Staff.$watch(function(event){
var allArray = Staff.getBySpecialtyId(specialtyId);
hasConsults = getHasConsults(allArray);
// Sort the lists
allArray.sort(sortList);
$scope.allStaff = allArray;
if($scope.isConsultsActive){
$scope.staffList = allArray.filter(function(staff){
return staff.is_consult;
});
}
});
...
And my services.js has the following services defined:
.service('Staff', function($firebaseArray, $window){
var staffList = $firebaseArray.$extend({
...
});
var ref = new $window.Firebase(FIREBASE_URL).child("staff");
var syncArray = new staffList(ref);
return syncArray;
})
.service('Teams', function($firebaseArray, $window){
var staffList = $firebaseArray.$extend({
...
});
var ref = new $window.Firebase(FIREBASE_URL).child("teams");
var syncArray = new staffList(ref);
return syncArray;
})
The $scope.specialty is the object that doesn't always seem to get filled. I am having the same problem on the teams page as well, but the example is almost exactly the same as this.

Resources