AngularJS + Firebase.util. How do I access data without knowing parent key? - javascript

Background:
When a delivery is created, an entry will be created under:
1- DELIVERIES->COMPANY->FIREBASEGENERATEDID->DELIVERY DETAILS
To allow users to track their purchases, they will have the delivery company and the unique ID of the delivery added to their profile.
2- USERS->DELIVERYTRACKING->COMPANY(delivery)->FIREBASEGENERATEDID(generated from deliveries above): TRUE
I am able to utilise Firebase.util.intersection to return an array of Firebase references, based on the use case above. However, when bound to a variable in $scope, I am unable to access the specific keys of the child object although I can display the lot to the view.
I've constructed a detailed JSFiddle which should explain the problem much more eloquently that I can by typing.
JSFiddle Link: http://jsfiddle.net/rwk1/8po8t35q/9/
SO required code:
<body ng-app="testApp" ng-controller="TestCtrl">
<div data-ng-repeat="tDelivery in trackedDelivery | orderByPriority">
<div class="profile-cards">
<p>{{tDelivery}}</p>
</div>
</div>
<br/><br/>
<h5>Question: How do I access the individual delivery details i.e. status?</h5>
</body>
'use strict';
var app = angular.module('testApp', [
'firebase'
]);
Controller:
app.controller('TestCtrl', function($scope, $firebase){
var fb = new Firebase('https://so0001.firebaseio.com');
var trackingRef = fb.child('users/Cara/deliveryTracking');
var trackingFb = $firebase(trackingRef);
trackingFb.$on('loaded', function(){
console.log(trackingFb.$getIndex());
});
var trackedDeliveryRefArray = [];
(function(){
trackingFb.$on('loaded', function(){
//console.log(trackingFb.$getIndex());
angular.forEach($firebase(trackingRef ).$getIndex(), function (k,v){
var trackedDeliveryRef = new Firebase.util.intersection(fb.child('users/Cara/deliveryTracking/' + k ),
fb.child('deliveries' + '/' + k ));
trackedDeliveryRefArray.push($firebase(trackedDeliveryRef));
});
});
})();
$scope.trackedDelivery = trackedDeliveryRefArray;
});
Result:
{"-randomFBKey1":{"company1":true,"name":"name value 1","status":"delivery 1 status"}}
{"-randomFBKey3":{"company2":true,"name":"delivery 3","status":"delivery 3 status"}}
Question: How do I access the individual delivery details i.e. status?
Structure:

Related

Display checkboxes in with AngularJS based on two object arrays

I know there are several similar topics already but I found none that really matches my problem.
When opening my AngularJS app/website I am loading two arrays of objects (both are of the same type). One list contains all possible values (called sources here) and the other list is a selection of elements from the first.
My goal is display all sources as checkboxes. The ones from the second list have to be preselected, the rest not. Now the user can select/deselect checkboxes. If he does so I need to inform the server (with the source.id).
What I got so far:
exampleApp.controller('testController', [ '$scope', '$http', function($scope, $http) {
$scope.sources = [];
$scope.selectedSources = [];
$scope.changeSource = function(source) {...};
})
and
<div ng-repeat="source in sources">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="??"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
What I can't figure out is how I can get ng-model to preselect the right checkboxes and how to get the new (and old) values to changeSource(). Is there an elegant way of doing that?
Example (Pseudo code only):
Sources = [{id=1, name=test1},{id=2, name=test2}, ...]
SelectedSources = [{id=2, name=test2}]
Now what I need are checkboxes like this:
[ ] test1 [x] test2
where all elements from sources are checkboxes and the ones from selectedsources are preselected. Changes of the selection can be stored in selected sources (as objects) and have to trigger my changeSource() function so that I can inform my server.
Set the selected/unselected state in a property inside each of the objects in Sources array(initialize it based on whats present in selectedArray)
$scope.sources.forEach(function(source) {
source.selected = isSelected(source);
})
function isSelected(selectedSource) {
return !!$scope.selectedSources.find(function(s) {
return s === selectedSource || s.id == selectedSource.id;
})
}
Here's a working plunker link
I didn't understood your question very well, but if i'm not mistaken, you want to fill the second collection only with the selected items from the first one, right? If it's the case, you could turn your second collection into a returning function with a filter of the first inside, as follows:
In your controller:
exampleApp.controller('testController', [ '$scope', '$http', function ($scope, $http) {
$scope.sources = [];
/* ... */
$scope.getSelectedSources = function () {
return $scope.sources.filter(function (val) {
return val.selected;
});
};
})
In your template:
<div ng-repeat="source in sources">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="source.selected"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
<div ng-repeat="source in getSelectedSources()">
<input
type="checkbox"
name="source.name"
value="{{source.id}}"
ng-model="source.selected"
ng-change="changeSource(source.id)"
> {{source.name}}
</div>
Hi this may be help you to get new & old value.
$scope.$watch('sources', function (oldval, newval) {
console.log(oldval + newval);
});

how to trigger an event in controller2 based on an action in controller1

Here is the plunkr i have created.
Basically i am facing 2 issues with this piece of code -
I need help loading months in the drop down and
When month is changed in the dropdown from headerController, the sales for that month is displayed in detailController. I am trying to create a dependency between multiple controllers using a service.
I will appreciate any help fixing these 2 issues.
You can use $broadcast service for event purposes. Following is the link which explains the use of $broadcast and communicating between two controllers.
enter code herehttp://plnkr.co/edit/d98mOuVvFMr1YtgRr9hq?p=preview
You could simply achieve this by using $broadcast from one controller in $rootScope and listen that event in $scope using $on. Though I would suggest you to use service that will share data among to controller. Using dot rule will reduce your code. Take a look at below optimized code. Also you could replace your select with ng-repeat with ng-option to save object on select.
Markup
<div data-ng-controller="headerController">
<h3>Select Month</h3>
<select id="month" ng-model="sales.selectedMonth"
ng-options="month.monthId for month in sales.monthlySales">
</select>
</div>
<div data-ng-controller="detailsController">
<h3>Sales for Month</h3>
<div ng-bind="sales.selectedMonth"></div>
</div>
Code
app.service('valuesService', [function() {
var factory = {};
factory.sales = {}
factory.sales.salesForMonthId = 10;
factory.sales.months = [1, 2];
factory.sales.monthlySales = [{monthId: 1,sales: 10}, {monthId: 2,sales: 20}];
factory.sales.selectedMonth = factory.sales.monthlySales[0];
return factory;
}]);
app.controller('headerController', ['$scope', 'valuesService',
function($scope, valuesService) {
$scope.sales = {};
getData();
function getData() {
$scope.sales = valuesService.sales;
}
}
]);
app.controller('detailsController', ['$scope', 'valuesService',
function($scope, valuesService) {
$scope.sales = {};
getData();
function getData() {
$scope.sales = valuesService.sales;
}
}
]);
Demo Plunkr
I can see the months are already loading fine.
For proper data binding to work across service and controller, you would need to bind one level above the actual data, resulting a dot in your expression. This is because javascript doesn't pass by reference for primitive type.
In service:
factory.data = {
salesForMonthId: 0
}
In controller:
app.controller('detailsController', ['$scope', 'valuesService',
function ($scope, valuesService) {
$scope.values = valuesService.data;
}
]);
In template:
<div>{{values.salesForMonthId}}</div>
Plunker

Ng-model with Cookie

I'm trying to take the first example from the angular.js homepage and adding in cookie support.
This is what I have so far: https://jsfiddle.net/y7dxa6n8/8/
It is:
<div ng-app="myApp">
<div ng-controller="MyController as mc">
<label>Name:</label>
<input type="text" ng-model="mc.user" placeholder="Enter a name here">
<hr>
<h1>Hello {{mc.user}}!</h1>
</div>
</div>
var myApp = angular.module('myApp', ['ngCookies']);
myApp.controller('MyController', [function($cookies) {
this.getCookieValue = function () {
$cookies.put('user', this.user);
return $cookies.get('user');
}
this.user = this.getCookieValue();
}]);
But it's not working, ive been trying to learn angular.
Thanks
I'd suggest you create a service as such in the app module:
app.service('shareDataService', ['$cookieStore', function ($cookieStore) {
var _setAppData = function (key, data) { //userId, userName) {
$cookieStore.put(key, data);
};
var _getAppData = function (key) {
var appData = $cookieStore.get(key);
return appData;
};
return {
setAppData: _setAppData,
getAppData: _getAppData
};
}]);
Inject the shareDataService in the controller to set and get cookie value
as:
//set
var userData = { 'userId': $scope.userId, 'userName': $scope.userName };
shareDataService.setAppData('userData', userData);
//get
var sharedUserData = shareDataService.getAppData('userData');
$scope.userId = sharedUserData.userId;
$scope.userName = sharedUserData.userName;
Working Fiddle: https://jsfiddle.net/y7dxa6n8/10/
I have used the cookie service between two controllers. Fill out the text box to see how it gets utilized.
ok, examined your code once again, and here is your answer
https://jsfiddle.net/wz3kgak3/
problem - wrong syntax: notice definition of controller, not using [] as second parameter
If you are using [] in controller, you must use it this way:
myApp.controller('MyController', ['$cookies', function($cookies) {
....
}]);
this "long" format is javascript uglyfier safe, when param $cookies will become a or b or so, and will be inaccessible as $cookies, so you are telling that controller: "first parameter in my function is cookies
problem: you are using angular 1.3.x, there is no method PUT or GET in $cookies, that methods are avalaible only in angular 1.4+, so you need to use it old way: $cookies.user = 'something'; and getter: var something = $cookies.user;
problem - you are not storing that cookie value, model is updated, but cookie is not automatically binded, so use $watch for watching changes in user and store it:
$watch('user', function(newValue) {
$cookies.user = newValues;
});
or do it via some event (click, submit or i dont know where)
EDIT: full working example with $scope
https://jsfiddle.net/mwcxv820/

Setting up AngularJS template with API javascript

I am new to AngularJS and want to convert my current website to AngularJS. Below is a section of my webpage that shows meetings pulled from Google calendar. I am using the API to do this. My questions is how would I convert the HTML/Javascript to an AngularJS template? Do I just use a controller and dump all the javascript in it?
Currently my HTML shows the first two results in my calendar list.
This is my HTML:
<section class="sub-box meetings-box">
<div class="meetings-section">
<span class="meeting-h1">NEXT MEETING</span>
<div class="next-meetings-section">
<div class="meeting-info meeting-time next-meeting-time-start"></div>
<div class="meeting-info meeting-time next-meeting-time-end"></div>
<div class="meeting-info next-meeting-title"></div>
<div class="meeting-info next-meeting-location"></div>
</div>
<span class="meeting-h2">UPCOMING MEETINGS</span>
<div class="upcoming-meetings-section">
<div class="meeting-info meeting-time second-meeting-time-start"></div>
<div class="meeting-info meeting-time second-meeting-time-end"></div>
<div class="meeting-info second-meeting-title"></div>
<div class="meeting-info second-meeting-location"></div>
</div>
</section>
This is part of my Javascript that shows the callback response API
request.then(function(callbackResponse) {
var entries = callbackResponse.result.items; //returns an array entries
//get meeting info
var nextMeeting = entries[0];
var nextMeetingTimeStart = nextMeeting.start;
var nextMeetingTimeEnd = nextMeeting.end;
var nextMeetingTitle = nextMeeting.summary;
var nextMeetingLocation = nextMeeting.location;
var secondMeeting = entries[1];
var secondMeetingTimeStart = secondMeeting.start;
var secondMeetingTimeEnd = secondMeeting.end;
var secondMeetingTitle = secondMeeting.summary;
var secondMeetingLocation = secondMeeting.location;
//formatting info
for (var x in nextMeetingTimeStart && nextMeetingTimeEnd &&
secondMeetingTimeStart && secondMeetingTimeEnd) {
var nextMeetingStart = nextMeetingTimeStart[x];
var nextMeetingEnd = nextMeetingTimeEnd[x];
var secondMeetingStart = secondMeetingTimeStart[x];
var secondMeetingEnd = secondMeetingTimeEnd[x];
var nextMeetingStartFormat = new Date(nextMeetingStart).toString('hh:mm tt');
var nextMeetingEndFormat = new Date(nextMeetingEnd).toString('hh:mm tt');
var secondMeetingStartFormat = new Date(secondMeetingStart).toString('hh:mm tt');
var secondMeetingEndFormat = new Date(secondMeetingEnd).toString('hh:mm tt');
$('.next-meetings-section').find('.next-meeting-time-start').text(nextMeetingStartFormat+'-');
$('.next-meetings-section').find('.next-meeting-time-end').text(nextMeetingEndFormat);
$('.upcoming-meetings-section').find('.second-meeting-time-start').text(secondMeetingStartFormat+'-');
$('.upcoming-meetings-section').find('.second-meeting-time-end').text(secondMeetingEndFormat);
}
$('.next-meetings-section').find('.next-meeting-title').text(nextMeetingTitle);
$('.next-meetings-section').find('.next-meeting-location').text(nextMeetingLocation);
$('.upcoming-meetings-section').find('.second-meeting-title').text(secondMeetingTitle);
$('.upcoming-meetings-section').find('.second-meeting-location').text(secondMeetingLocation);
With Angular you would want to use a directive for DOM Manipulation, a factory (there are other options but this is a good starting place) for http calls and to store data. The controller should be concerned with providing scope for the view.
Angular Documentation:
controllers
directives
providers - factories are included here.
You may want to spend a few hours going through a tutorial or two before refactoring into Angular. Code School has a good one for free.
Edit-
Looking at your code you would want to take care of the server response inside a factory - create properties on your factory for nextMeeting and secondMeeting, and have them set to your server response data each time you get a server response back with the data.
Inject your factory into a controller, then in your controller you can have properties your view will use like: nextMeetingStart, nextMeetingEnd, etc. The value of these properties can be functions that use the values on your factory's nextMeeting and secondMeeting properties to set their appropriate return values.
Then you can just reference those properties in your view. The values displayed in the view will update whenever the factory receives new data from the server.

How to add items to a specific dictionary within an item in a list with AngularFire?

The below code shows a list from firebase and shows a corresponding comment field for each item in the list. The user can make a comment on that item and it will update the comment field for that item in the list. Currently, each time a comment is made, it overwrites the previous one, but I'd like for all comments to be saved.
How do I make it so that every time a comment is added, the previous ones are saved as well?
http://jsfiddle.net/chrisguzman/PS9J2/
indx.html
<div ng-app="MyApp" ng-controller="MyCtrl">
<div ng-repeat="(id,item) in data">
<h2>{{item.title}}</h2>
<input ng-model="item.comment"></input>
<button type="submit" ng-click="CommentAdd(id)">Comment</button>
</div>
</div>
app.js
angular.module('MyApp', ['firebase'])
.controller('MyCtrl',
function MyCtrl($scope, $firebase) {
var furl = "https://helloworldtest.firebaseio.com";
var ref = new Firebase(furl);
$scope.data = $firebase(ref);
$scope.CommentAdd = function (id) {
$scope.data.$save(id);
};
});
The following is the data structure within firebase that is generated
{helloworldtest:
{-JSQhsAnY5zhf0oVKfbb: {title: "nameA", comment:"Second Comment"},
-JSQhsAnY5zhf0oVKfbb: {title: "nameB", comment:"Second Comment"}}
}
However, I'd like to create the following where there is a 'comments' branch that holds all comments.
{helloworldtest:
{-JSQhsAnY5zhf0oVKfbb: {title: "nameA", comments:{-JSQhsAnY5zhf0oVKfbb:{Comment:"Second Comment"},-JSQhsAnY5zhf0oVKfbb:{Comment:"First Comment"}}},
{-JSQhsAnYdfdfdffbb: {title: "nameA", comments:{-JSQhsAnY5zhf0oVKfAb:{Comment:"Another Comment"},-JSQhsAnY5zhf0oVKfbb:{Comment:"First Comment"}}}
}
I've tried to do this by replacing
$scope.data.$save(id);
with
$scope.data.$add(id);
I've also tried using :
$scope.data[id].$add({foo: "bar"})
You are saving the comment into a field called comment. Instead, use a list called comments and utilize push or $add.
<div ng-app="MyApp" ng-controller="MyCtrl">
<div ng-repeat="(id,item) in data">
<h2>{{item.title}}</h2>
<input ng-model="newComment"></input>
<button type="submit" ng-click="addComment(id, newComment)">Comment</button>
</div>
</div>
function MyCtrl($scope, $firebase) {
var furl = "https://helloworldtest.firebaseio.com";
var ref = new Firebase(furl+'/items');
$scope.data = $firebase(ref);
var $comments = $firebase( commentsRef );
$scope.addComment = function (id, newComment) {
ref.child(id).child('comments').push(newComment);
};
});
Also Don't nest data just because you can. Instead, consider putting comments in their own path, items in their own path.

Categories

Resources