Submit multiple checkbox to firebase - angularjs

I am using Angularfire and I'd like to save data by multiple checkbox.
HTML
<form role="form" ng-submit="addTask(task)">
<label class="checkbox-inline" ng-repeat="(key, value) in students">
<input type="checkbox" id="{{key}}" value="{{key}}" ng-model="task.student[value.name]">{{value.name}}
</label>
<button type="submit">Submit</button>
</form>
JS
var ref = new Firebase(FURL);
var fbTasks = $firebaseArray(ref.child('tasks'));
$scope.addTask = function(task) {
fbTasks.$add(task);
}
This was the result
student
--Alison Kay: true
--Jessica Cook:false
--John Smith: true
--Kevin Hunt: true
My question is there any way to save them like this?
student
--(key)
--name:Alison Kay
--checked: true
--(key)
--name:Jessica Cook
--checked: false
--(key)
--name:John Smith
--checked: true
--(key)
--name:Kevin Hunt
--checked: true

I threw together a rough example PLNKR to demonstrate one way to do this by extending the AngularFire services.
Note that the documentation states:
These techniques should only be attempted by advanced Angular users who know their way around the code.
Solution
You can create a factory that extends $firebaseObject, and adds a method .addTask() which uses .push() to generate a new key for a new task.
Factories:
app.factory("TaskList",function($rootScope, $q, fbUrl, TaskListFactory){
return function(studentKey){
var ref = new Firebase(fbUrl+'/tasks/'+studentKey);
return new TaskListFactory(ref);
}
});
app.factory("TaskListFactory",function($firebaseObject, $q, fbUrl, $rootScope){
return $firebaseObject.$extend({
addTask: function(name, checked){
// use push to generate a new key, set `name` and `checked`
this.$ref().push({name: name, checked: checked}, function(error){
if(!error){
console.error(error);
} else {
console.log("Pushed new task.");
}
});
}
});
});
Controller:
Note: I used mock objects. I couldn't decode your data structure, and took a best guess approach.
app.controller('HomeController',function($scope,fbUrl, $firebaseObject, TaskList) {
// create mock student and task
$scope.students = {tester: {name: 'tester'} };
$scope.task = {tester: {name: 'test this'}};
var taskList = new TaskList('student123');
// get tasks list for debug:
var tasksRef = new Firebase(fbUrl+'/tasks');
$scope.tasks = $firebaseObject(tasksRef);
$scope.addTask = function(task) {
console.debug(task);
taskList.addTask('Tester McGee', task.student['tester']);
}
});
Result (<firebaseUrl>/tasks):
{
"$id": "tasks",
"$priority": null,
"student123": {
"-JoMxWoX0tQrGtdP6Qvm": {
"checked": true,
"name": "Tester McGee"
}
}
}
Again, the focus of this is on the factories, and not on the data structure. The form data in my example doesn't make sense.
Hope that helps.

Related

AngularJs Component and angularjs-dropdown-multiselect

I am new to AngularJs world and was trying to use angularjs-dropdown-multiselect inside component.
Component's HTML looks like:
<div>
<select id="statusSelection" class="form-control"
ng-options="status.name for status in $ctrl.statuses track by status.id"
ng-model="$ctrl.status" ng-change="$ctrl.filterChanged()"></select>
</div>
<div ng-dropdown-multiselect="" options="$ctrl.categories" selected-model="$ctrl.category"
events="$ctrl.events">
</div>
Event on status changed and category will call the same action.
MultiselectController.prototype.filterChanged = function() {
this.onFilterChange({
status: this.status,
category: this.category});
};
MultiselectController.prototype.events = {
onItemSelect: function(item) {
filterChanged();
},
onItemDeselect: function(item) {
filterChanged();
}
};
When I try to run the above code and change the status, the code works as expected but fails during Category change(error in console).
Error message: ReferenceError: filterChanged is not defined
Angular version: 1.5.8
angularjs-dropdown-multiselect: 1.11.8
Plunker: http://plnkr.co/edit/JL7N6M?p=preview
Thanks for helping me out here.
I have created an instance variable and initialize it to instance of Controller.
var _instance;
function MultiselectController($scope) {
this.statuses = testMultiselect.statuses;
this.categories = testMultiselect.categories;
this.$scope = $scope;
this.setDefault();
_instance = this;
}
Now, I am using this instance variable to access the functions on Controller.
MultiselectController.prototype.events = {
onItemSelect: function(item) {
_instance.filterChanged();
},
onItemDeselect: function(item) {
_instance.filterChanged();
}
};
I am not completely happy with this as there should be better way to do the same but until I find, I will keep this.
Updated Plunker: http://plnkr.co/edit/D7BKI9?p=preview

Insert data into existing child firebase

First of all, I want to apologize for my poor English.
The last week I started to explore AngularJS in the Ionic framework with Firebase as backend. But I’ve stumbled upon a problem that I can’t solve for the past 3 days.
The purpose is when I chose a storage place and confirm it, an inventory will be created with a date and a link to the storage place to know to which storage place the inventory belongs. After that a list of products will be shown. When you chose a product, a new screen appears where you can add the amount of that product.
My problem is, I can create an inventory after confirmation, but it seems like I can’t insert the product with the amount inside the just created inventory.
Before insertion:
- Inventories
- Date: 1449767729166
- storage: "My Storage place 1"
I want my database looks like this after the insert:
- Inventories
- Date: 1449767729166
- storage: "My Storage place 1"
- Products:
- Name: Heineken
- Boxes: 12
- Bottles: 6
Here is my code:
addProducts.html
<div class="list">
<label class="item item-input item-floating-label">
<span class="input-label"><p>Aantal volle bakken/boxes</p></span>
<input type="text" placeholder="Aantal volle bakken/boxes" ng-model="products.boxes">
</label>
<br>
<label class="item item-input item-floating-label">
<span class="input-label"><p>Aantal (losse) flessen</p></span>
<input type="text" placeholder="Aantal (losse) flessen" ng-model="products.flessen">
</label>
<br>
<button class="button button-block button-dark" align="left" ng-click="AddProducts(products)">
Toevoegen
</button>
</div>
controllers.js:
.controller('AddProductCtrl', function($scope, $state, Inventory, Product) {
$scope.products = Inventory;
$scope.products = Product;
var now = Firebase.ServerValue.TIMESTAMP;
$scope.AddProducts = function(products) {
var query = Product.orderByChild(products.storage).equalTo('now');
query.once('child_added', function(snapshot) {
snapshot.ref().child('Products').push({
'Boxes' : fullBox,
'Bottles': losFles
});
});
}})
services.js
.factory('Product', ['$firebaseArray', function($firebaseArray) {
var addProductRef = new Firebase('https://testdb-1.firebaseio.co/Inventories/');
return $firebaseArray(addProductRef); }])
My Firebase structure:
Firebase structure
This is the error I got:
Error: Product.orderByChild is not a function
I already tried serveral things but with no success.
I'm really new to this. So, please point out what I did wrong.
Thanks in advance!
Your Product factory returns a $firebaseArray, which does not have an orderByChild() method.
I can't really figure out your use-case, but one way to solve the error message:
.factory('Product', ['$firebaseArray', function($firebaseArray) {
var addProductRef = new Firebase('https://testdb-1.firebaseio.co/Inventories/');
return addProductRef;
}])
So instead of returning a $firebaseArray this returns a Firebase reference, which has the orderByChild method you're looking to use.
And then:
$scope.AddProducts = function(products) {
var query = Product.orderByChild(products.storage).equalTo('now');
query.once('child_added', function(snapshot) {
snapshot.ref().child('Products').push({
'Boxes' : fullBox,
'Bottles': losFles
});
});
I believe the confusion comes from mixing up the Firebase SDK with the AngularFire API.
You properly create Products factory using a $firebaseArray(), but then you try to use it like a regular Firebase reference.
A $firebaseArray() takes in a Firebase references and wraps all of the child events (child_added, child_changed, etc...) and create a synchronized array.
You can try using a factory to get the products:
.constant('FirebaseUrl', '<my-firebase-app>')
.service('rootRef', ['FirebaseUrl', Firebase])
.factory('productsByDate', function(rootRef, $q) {
return function productsByDate(storage) {
var deferred = $q.defer();
var productRef = rootRef.child('products'); // or however you get there
var localNow = new Date().getTime();
var query = Product.orderByChild(storage).equalTo(localNow);
query.once('value', function(snapshot) {
deferred.resolve(snapshot);
});
return deferred.promise;
};
});
Try this in your controller:
.controller('AddProductCtrl', function($scope, $state, Inventory, Product, productsByDate) {
$scope.products = Inventory;
$scope.products = Product;
var now = Firebase.ServerValue.TIMESTAMP;
$scope.AddProducts = function(products) {
// get your products by date
productsByDate(products.storage).then(function(snapshot) {
snapshot.ref().child('Products').push({
'Boxes' : fullBox,
'Bottles': losFles
});
});
});
});

Using MongoDB with Restangular

I'm new with MEAN and now I have a problem which I can't solve. On server side for providing a REST API with express I'm using library node-restful. So there I have this schema Sport.js:
var mongoose = require('mongoose');
// create a schema
var SportSchema = new mongoose.Schema({
name: {
type: String,
required: true
}
});
// export the model schema
module.exports = SportSchema;
and controller SportController.js
var restful = require('node-restful');
module.exports = function (app, route) {
// setup the controller for REST
var rest = restful.model(
'sport',
app.models.sport
).methods(['get', 'put', 'post', 'delete']);
// register the endpoint with the application
rest.register(app, route);
// return middleware
return function (req, res, next) {
next();
};
};
On the client side I'm using library restangular as AngularJS service to handle Rest API Restful Resources. Here is controller main.js which use it:
angular.module('clientApp')
.controller('MainCtrl', function ($scope, Sport) {
$scope.sports = Sport.getList().$object;
console.log($scope);
});
In Firebug I see that object $scope so that works fine and also I can use object $scope.sports in my template. But what I want here is using mongoosejs' commands, for example
angular.module('clientApp')
.controller('MainCtrl', function ($scope, Sport) {
// find each sport with a name matching 'Run'
Sport.findOne({ 'name': 'Run' }, function (err, sport) {
if (err) return handleError(err);
console.log(sport.name); // <-- does not work
});
$scope.sports = Sport.getList().$object;
//console.log($scope);
});
Is it possible to do it? I'm really new in MEAN so I apologize if I'm doing something totally wrong.
So you have a functional MongoDB and you now just want to list a sport that contains 'Run'?
Put a ng-model in the html element that is supposed to do something with the output. Say you want to list all the sports containing run in a list:
<any-html ng-model="findonly.name" value="Run">
Or perhaps as a modifiable box:
<input ng-model="searchSport.name" placeholder="Search for sports by name">
Which you can then use as an Angular filter
<table> <th>...</th>
<tr ng-repeat="Sport in sports" | filter:searchSport.name:strict"> <td> {{Sport.name}}
</td></tr></table>
or in case of the first example use
filter:findonly.name:strict"

How to pass data to angularJs to server

I have some in put in my form.
<input type="text" name = "alias" placeholder="Start Time" style="width:200px"/><br/>
<button ng-click="new()" name="new" >Add</button>
How can I pass input value to server to make new user in mongoDb.
The client controller:
$scope.new = function(){
//$scope.user.$save({ id: $scope.user._id }, function() {
// $scope.users = User.query();
// how can I pass from here
});
}
I can get this value in the server controller.js
function json_user_save(id) {
var self = this;
// self.model('user').Schema;
// framework.model('user').Schema;
var User = MODEL('user').Schema;
console.log('save ->', id);
// What is it? https://github.com/totaljs/examples/tree/master/changes
self.change('user: save, id: ' + id);
var model = self.body;
var user = new User({ alias: model.alias, created: new Date() }).save(function(err) {
if (err)
self.throw500(err);
// Read all users
User.find(self.callback());
});
}
How can I pass value from angularjs to server?
User $http.post method (https://docs.angularjs.org/api/ng/service/$http) and ng-submit directive (https://docs.angularjs.org/guide/forms)
An even better solution would be the use the Restangular library on top of Angular.
https://github.com/mgonto/restangular#collection-methods

Proper place for data-saving logic in AngularJS

App design question. I have a project which has a very large number of highly customized inputs. Each input is implemented as a directive (and Angular has made this an absolute joy to develop).
The inputs save their data upon blur, so there's no form to submit. That's been working great.
Each input has an attribute called "saveable" which drives another directive which is shared by all these input types. the Saveable directive uses a $resource to post data back to the API.
My question is, should this logic be in a directive at all? I initially put it there because I thought I would need the saving logic in multiple controllers, but it turns out they're really happening in the same one. Also, I read somewhere (lost the reference) that the directive is a bad place to put API logic.
Additionally, I need to introduce unit testing for this saving logic soon, and testing controllers seems much more straightforward than testing directives.
Thanks in advance; Angular's documentation may be… iffy… but the folks in the community are mega-rad.
[edit] a non-functional, simplified look at what I'm doing:
<input ng-model="question.value" some-input-type-directive saveable ng-blur="saveModel(question)">
.directive('saveable', ['savingService', function(savingService) {
return {
restrict: 'A',
link: function(scope) {
scope.saveModel = function(question) {
savingService.somethingOrOther.save(
{id: question.id, answer: question.value},
function(response, getResponseHeaders) {
// a bunch of post-processing
}
);
}
}
}
}])
No, I don't think the directive should be calling $http. I would create a service (using the factory in Angular) OR (preferably) a model. When it is in a model, I prefer to use the $resource service to define my model "classes". Then, I abstract the $http/REST code into a nice, active model.
The typical answer for this is that you should use a service for this purpose. Here's some general information about this: http://docs.angularjs.org/guide/dev_guide.services.understanding_services
Here is a plunk with code modeled after your own starting example:
Example code:
var app = angular.module('savingServiceDemo', []);
app.service('savingService', function() {
return {
somethingOrOther: {
save: function(obj, callback) {
console.log('Saved:');
console.dir(obj);
callback(obj, {});
}
}
};
});
app.directive('saveable', ['savingService', function(savingService) {
return {
restrict: 'A',
link: function(scope) {
scope.saveModel = function(question) {
savingService.somethingOrOther.save(
{
id: question.id,
answer: question.value
},
function(response, getResponseHeaders) {
// a bunch of post-processing
}
);
}
}
};
}]);
app.controller('questionController', ['$scope', function($scope) {
$scope.question = {
question: 'What kind of AngularJS object should you create to contain data access or network communication logic?',
id: 1,
value: ''
};
}]);
The relevant HTML markup:
<body ng-controller="questionController">
<h3>Question<h3>
<h4>{{question.question}}</h4>
Your answer: <input ng-model="question.value" saveable ng-blur="saveModel(question)" />
</body>
An alternative using only factory and the existing ngResource service:
However, you could also utilize factory and ngResource in a way that would let you reuse some of the common "saving logic", while still giving you the ability to provide variation for distinct types of objects / data that you wish to save or query. And, this way still results in just a single instantiation of the saver for your specific object type.
Example using MongoLab collections
I've done something like this to make it easier to use MongoLab collections.
Here's a plunk.
The gist of the idea is this snippet:
var dbUrl = "https://api.mongolab.com/api/1/databases/YOURDB/collections";
var apiKey = "YOUR API KEY";
var collections = [
"user",
"question",
"like"
];
for(var i = 0; i < collections.length; i++) {
var collectionName = collections[i];
app.factory(collectionName, ['$resource', function($resource) {
var resourceConstructor = createResource($resource, dbUrl, collectionName, apiKey);
var svc = new resourceConstructor();
// modify behavior if you want to override defaults
return svc;
}]);
}
Notes:
dbUrl and apiKey would be, of course, specific to your own MongoLab info
The array in this case is a group of distinct collections that you want individual ngResource-derived instances of
There is a createResource function defined (which you can see in the plunk and in the code below) that actually handles creating a constructor with an ngResource prototype.
If you wanted, you could modify the svc instance to vary its behavior by collection type
When you blur the input field, this will invoke the dummy consoleLog function and just write some debug info to the console for illustration purposes.
This also prints the number of times the createResource function itself was called, as a way to demonstrate that, even though there are actually two controllers, questionController and questionController2 asking for the same injections, the factories get called only 3 times in total.
Note: updateSafe is a function I like to use with MongoLab that allows you to apply a partial update, basically a PATCH. Otherwise, if you only send a few properties, the entire document will get overwritten with ONLY those properties! No good!
Full code:
HTML:
<body>
<div ng-controller="questionController">
<h3>Question<h3>
<h4>{{question.question}}</h4>
Your answer: <input ng-model="question.value" saveable ng-blur="save(question)" />
</div>
<div ng-controller="questionController2">
<h3>Question<h3>
<h4>{{question.question}}</h4>
Your answer: <input ng-model="question.value" saveable ng-blur="save(question)" />
</div>
</body>
JavaScript:
(function() {
var app = angular.module('savingServiceDemo', ['ngResource']);
var numberOfTimesCreateResourceGetsInvokedShouldStopAt3 = 0;
function createResource(resourceService, resourcePath, resourceName, apiKey) {
numberOfTimesCreateResourceGetsInvokedShouldStopAt3++;
var resource = resourceService(resourcePath + '/' + resourceName + '/:id',
{
apiKey: apiKey
},
{
update:
{
method: 'PUT'
}
}
);
resource.prototype.consoleLog = function (val, cb) {
console.log("The numberOfTimesCreateResourceGetsInvokedShouldStopAt3 counter is at: " + numberOfTimesCreateResourceGetsInvokedShouldStopAt3);
console.log('Logging:');
console.log(val);
console.log('this =');
console.log(this);
if (cb) {
cb();
}
};
resource.prototype.update = function (cb) {
return resource.update({
id: this._id.$oid
},
angular.extend({}, this, {
_id: undefined
}), cb);
};
resource.prototype.updateSafe = function (patch, cb) {
resource.get({id:this._id.$oid}, function(obj) {
for(var prop in patch) {
obj[prop] = patch[prop];
}
obj.update(cb);
});
};
resource.prototype.destroy = function (cb) {
return resource.remove({
id: this._id.$oid
}, cb);
};
return resource;
}
var dbUrl = "https://api.mongolab.com/api/1/databases/YOURDB/collections";
var apiKey = "YOUR API KEY";
var collections = [
"user",
"question",
"like"
];
for(var i = 0; i < collections.length; i++) {
var collectionName = collections[i];
app.factory(collectionName, ['$resource', function($resource) {
var resourceConstructor = createResource($resource, dbUrl, collectionName, apiKey);
var svc = new resourceConstructor();
// modify behavior if you want to override defaults
return svc;
}]);
}
app.controller('questionController', ['$scope', 'user', 'question', 'like',
function($scope, user, question, like) {
$scope.question = {
question: 'What kind of AngularJS object should you create to contain data access or network communication logic?',
id: 1,
value: ''
};
$scope.save = function(obj) {
question.consoleLog(obj, function() {
console.log('And, I got called back');
});
};
}]);
app.controller('questionController2', ['$scope', 'user', 'question', 'like',
function($scope, user, question, like) {
$scope.question = {
question: 'What is the coolest JS framework of them all?',
id: 1,
value: ''
};
$scope.save = function(obj) {
question.consoleLog(obj, function() {
console.log('You better have said AngularJS');
});
};
}]);
})();
In general, things related to the UI belong in a directive, things related to the binding of input and output (either from the user or from the server) belong in a controller, and things related to the business/application logic belong in a service (of some variety). I've found this separation leads to very clean code for my part.

Resources