Currently, I am using ng-model to insert data into my MongoDB. Is there a way to use ng-model to insert data into an array in MongoDB? answers is an array that should contain 4 strings that the user enters. I tried adding [0], [1], [2], [3] to quiz.quizData.answers but it did not enter the data correctly as an array. Instead it entered it like this:
"answers" : [
{
"0" : "My First Answer",
"1" : "My Second Answer"
}
],
instead of how it should be:
"answers" : [
"My First Answer",
"My Second Answer"
],
My HTML input form:
<input type="text" name="answers" ng-model="quiz.quizData.answers" placeholder="enter answers here" required>
<input type="text" name="answers2" ng-model="quiz.quizData.answers" placeholder="enter other answers here" required>
My End Point
// POST request for users to post a new quiz entry
apiRouter.route('/quiz')
.post(function(req, res) {
// Create quiz object and assign to 'quiz'
var quiz = new Quiz();
// Contains the quiz question
quiz.question = req.body.question;
// Contains an array with four quiz answers
quiz.answers = req.body.answers;
// Contains one string that matches the correct answer from the array above
quiz.correctAnswer = req.body.correctAnswer;
// Identifies user creating the quiz
quiz.postedBy = req.body.postedBy;
// Identifies the category of the quiz
quiz.category = req.body.category;
// then save new quiz to database
quiz.save(function(err) {
// If an error occurs, display error message in JSON format
if (err) {
return res.json({ success: false, message: 'something went way wrong....' + err });
} else {
// If no error occurs and it saves, display success
res.json({ message: 'Quiz Created!' });
}
});
});
My MongoDB Schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// post schema
var QuizSchema = new Schema({
question: { type: String, lowercase: true, required: true },
answers: { type: Array, required: true },
correctAnswer: { type: String, required: true },
category: { type: String, lowercase: true, required: true },
postedBy: { type: String, required: true }
});
module.exports = mongoose.model('Quiz', QuizSchema);
You can just use ng-repeat iterating a range if you're starting with an empty array:
<input ng-repeat="i in [1,2,3,4]" type="text" name="answers{{i}}" ng-model="quiz.quizData.answers[i]" placeholder="enter answers here" required>
If your array already exists, and you want to account for variable array length you can use ng-repeat and $index. Make sure you add track by $index or you will encounter an issue with duplicate keys:
<input ng-repeat="answer in quiz.quizData.answers track by $index" type="text" name="answers{{$index}}" ng-model="quiz.quizData.answers[$index]" placeholder="enter answers here" required>
https://plnkr.co/edit/dwHIFTftgq5LFfTOpc9X?p=info
HTML
<input type="text" name="answer1" ng-model="answersArray[0]" placeholder="answer 1">
<input type="text" name="answer2" ng-model="answersArray[1]" placeholder="answer 2">
<input type="text" name="answer3" ng-model="answersArray[2]" placeholder="answer 3">
<input type="text" name="answer4" ng-model="answersArray[3]" placeholder="answer 4">
<input type="hidden" name="answers" ng-model="quiz.quizData.answers" placeholder="enter answers here" ng-init="quiz.quizData.answers = answersArray">
Controller
$scope.answersArray = [
answer1 = $scope.answer1,
answer2 = $scope.answer2,
answer3 = $scope.answer3,
answer4 = $scope.answer4
];
Related
I have inputs that build from objects in array.
Everything got right but when input.type = 'file', Angular change it to text type and i cant figure it out.
Did anything notice this?
My template:
<span ng-repeat="input in formInputs">
<label for="{{input.id}}">{{input.label}}</label>
<input type="{{input.type}}" id="{{input.id}}" name="{{input.name}}" ng-model="input.insert" ng-required="input.must">
</span>
My array:
var formInputs = [
{
label : 'first name',
id : 'id1',
type : 'text',
name : 'name1',
must : true,
insert : ''
},
{
label : 'upload file',
id : 'id2',
type : 'file',
name : 'name2',
must : true,
insert : ''
}
]
My result:
<span ng-repeat="input in formInputs">
<label for="id1">first name</label>
<input type="text" id="id1" name="name1" ng-model="input.insert" ng-required="input.must">
<label for="id2">upload file</label>
<input type="text" id="id2" name="name2" ng-model="input.insert" ng-required="input.must">
</span>
EDIT:
I have this flowing:
<input type="{{childInput.type}}" id="{{childInput.id}}" name="{{childInput.name}}">
And this array:
var formInputs = [
{
id : 'id',
type : 'file',
name : 'name',
}
]
The resolute [only in Safari]:
<input type="text" id="id" name="name">
Why its happening?
Thanks for your help!
From the AngularJS Documentation for input:
Note: Not every feature offered is available for all input types. Specifically, data binding and event handling via ng-model is unsupported for input[file].
So it looks like Angular falls back to type="text". There are a lot of answers which bring solutions to this, check out:
ng-model for <input type=“file”/>
From that answer, here's a way to deal with a file input.
.directive("fileread", [function () {
return {
scope: {
fileread: "="
},
link: function (scope, element, attributes) {
element.bind("change", function (changeEvent) {
var reader = new FileReader();
reader.onload = function (loadEvent) {
scope.$apply(function () {
scope.fileread = loadEvent.target.result;
});
}
reader.readAsDataURL(changeEvent.target.files[0]);
});
}
}
}]);
As mentionned by Hackerman, his jsfiddle seem to work (with Angular 1.0.1) at first sight, but it doesn't seem to populate the model correctly.
I have an array with many "contact" objects inside. Only one contact can be the primary contact (primary: true).
I also have a radio button to select which contact is the primary.
Q: How can I make one contact primary and deactivate all of the others (primary: false)? so only one object have the property (primary: true) and the rest false?
My example: http://plnkr.co/edit/Y3as4SXv2ZGQSF39W8O6?p=preview
.controller('ExampleController', ['$scope',
function($scope) {
$scope.addContList = [
{
email: "q#q.com",
jobTitle: "clerk",
name: "nico2",
phone: "1",
primary: true
},
{
email: "a#a.com",
jobTitle: "director",
name: "david",
phone: "1",
primary: false
}
];
$scope.$watch('addContList', function() {
console.log('changed', JSON.stringify($scope.addContList, null, 2))
}, true)
}
]);
Here is the view
<tr ng-repeat="contact in addContList">
<td>
<label class="radio-inline">
<input type="radio" value="" name="ui_cont" ng-model="contact.primary" ng-value="true">
</label>
</td>
<td>{{ contact.name }} value = {{contact.primary}} </td>
<td>Edit</td>
<td>Delete</td>
</tr>
You would want to add an ngChange event to your input and change all other inputs to false when one gets set to true. I have updated your Plnkr here: http://plnkr.co/edit/7gxI7if9nC7hAMQES1eu?p=preview
<input type="radio" value="" name="ui_cont" ng-change='changeOnPrimary(contact)' ng-model="contact.primary" ng-value="true">
Then in your controller:
$scope.changeOnPrimary = function(selectedContact) {
// iterate over your whole list
angular.forEach($scope.addContList, function(contact) {
// set primary to false for all contacts excepts selected
if (selectedContact.name !== contact.name) {
contact.primary = false;
}
});
}
Please note: the only reason I'm comparing the name field of the object is because there is no unique identifier to compare with. In real code, you would want to compare against an ID rather than a name field.
You can define a new scope property
$scope.primary = null
Then you can define a listener
$scope.$watch("primary", function(value) {
$scope.addContList.forEach(function(contact) {
contact.primary = angular.equals(value, contact);
})
})
and you can define a default value after defining the list
$scope.primary = $scope.addContList[0];
and in the html you change the input line in
<input type="radio" value="" name="ui_cont" ng-model="$parent.primary" ng-value="contact">
You need to use $parent.primary instead of primary, because ng-repeat defines a new child scope.
see http://plnkr.co/edit/5pvatBNwnrJhGzKhOIys?p=preview
I am learning the meteor-framework for it's ability to make realtime inputs and updates easily. Because it's new to me, I'm not quite sure if this is the most efficient way to do things.
I'm trying to create two forms to save some data in the MongoDB. I'm using SimpleSchema.
First there is a simple input field for adding a title to the DB.:
template
<template name="addForm">
<form>
<input type="text" name="text">
<input type="submit" value="add">
</form>
</template>
events.js
Template.addForm.events({
'submit form': function(event){
event.preventDefault();
var title = event.target.text.value;
MongoArticle.insert({
title: title,
slug: title.toLowerCase()
});
event.target.text.value = "";
}
});
collection.js
MongoArticle = new Mongo.Collection('articles');
var subset = new SimpleSchema({
age: {type: String, optional: true},
value: {type: String, optional: true},
info: {type: String, optional: true},
});
MongoArticle.attachSchema(new SimpleSchema({
title: {type: String},
slug: {type: String},
flag: {type: Boolean, optional: true},
elements: {type: [subset], optional: true}
}));
All titles saved in the DB will be displayed in a list:
<template name="list">
<ul>
{{#each art}}
<li>{{title}}</li>
{{/each}}
</ul>
</template>
If you click on the title you will get the detailed form. Here you can add and edit multiple elements, which are saved in a subdocument (elements -> subset).
<template name="Editor">
<table>
<tbody>
{{#each art.elements}}
<tr>
<td><input type="text" value="{{age}}"></td>
<td><input type="text" value="{{value}}"></td>
<td><input type="text" value="{{info}}"></td>
</tr>
{{/each}}
<tr>
<td><input type="text" value=""></td>
<td><input type="text" value=""></td>
<td><input type="text" value=""></td>
</tr>
</tbody>
</table>
</template>
So I'm trying to get all the elements of the selected article to be displayed in a row of input fields. That will allow them to be edited. I want to have realtime saving, so that (if possible) every change is saved without needing to click a button. Is that possible?
The last row should be used to input new data, which should then be added to the Editor.
I tried to do that with a update() on each row and an input() on the last row, but it didn't work for me, because I don't know how to save the row to the elements-field in the DB, which is a subdocument of the articles-collection.
So I read some websites and I found ´aldeed:autoform´ and ´aldeed:collection2´. Would this be helpful for what I am doing?
If you give your inputs names corresponding to your model fields then updating the existing elements boils down to the following snippet:
'change input[name]': function (e, t) {
var articleId = t.data.art._id; // or something similar
var updates = {};
var $target = $(e.target);
var index = $target.closest('tr').index();
var name = $target.attr('name');
updates['elements.' + index + '.' + name] = $target.val();
MongoArticle.update({ _id: articleId }, { $set: updates });
}
You may also consider using different events, e.g. keyup along with some _.debounce().
Inserting a new element is even simpler because you don't need the index. Also you will probably want to hook to different events (maybe button click?) and replace the $set modifier with $push, i.e.
MongoArticle.update({ _id: articleId }, { $push: {
elements: {
age: "",
value: "",
info: ""
}
}});
I'am new with node.js.I Need to save data from HTML page use - form (user name, password, email e.t.c)in Mongodb. I have this code:
var Schema = new mongoose.Schema({
_id : String,
name: String,
age : Number
});
var user = mongoose.model('emp', Schema);
app.post('/new', function(req, res){
new user({
_id : req.body.email,
name: req.body.name,
age : req.body.age
}).save(function(err, doc){
if(err) res.json(err);
else res.send('Successfully inserted!');
});
});
<form action="/new" method="POST">
<label for="email">Email: </label>
<input type="email" name="email" /><br />
<label for="name">Name: </label>
<input type="text" name="name" /><br />
<label for="age">Age: </label>
<input type="number" name="age" /><br />
<input type="submit"/>
When I run server. Trying to save some data. I see message
"Cannot read property 'email' of undefined"
"TypeError: Cannot read property 'email' of undefined"
Somebody help me please with this misunderstood. Thans a lot!
Also you don't need to define _id in your schema, it gets automatically created. Maybe you meant to define email : String so it matches your req.body.email
I'm trying to implement Mesosphere for validation into my meteor app but it seems like Mesosphere isn't picking up on some of the native validations I've listed.
I tried just a single validation for formatting of email and it's required length. For example:
Mesosphere({
name: 'signupForm',
method: 'signupUser',
fields: {
email: {
required: true,
format: 'email',
rules: {
exactLength: 4
},
message: 'Wrong length'
}
},
onFailure: function (errors) {
messages = [];
messages = _.map(errors, function (val, err) {
console.log(val.message);
});
},
onSuccess: function (data) {
alert("Totally worked!")
}
});
The 'onFailure' (and onSuccess) callback seems to work because it is logging something when I submit the form. Which makes me believe I have it set up properly on the form submit event too. There you pass the form object to Mesosphere to create the validationObject if I understand it correctly. For example:
var validationObject = Mesosphere.signupForm.validate(accountData);
Once submitted, it's logging Field Required as the error which is weird because I did type something into the field. It makes no mention of an incorrect length or format. It skips the 'Wrong Length' message and I can't find that message in the object anywhere.
So my question is what am I doing wrong to not be getting the proper message for the incorrect input for that form field? Thanks : )
Also, willing to take recommendations on other validation packages. Mesosphere leverages Meteor's server/client features for validation so it seemed like a good place to start.
Template:
<template name="signup">
<form name="signupForm" id="signup-form" class="panel" action="#">
<div class="form-group">
<label for="email">Email</label>
<input type="text" name="email" id="email" class="form-control" />
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" name="password" id="password" class="form-control" />
</div>
<div class="form-group">
<input type="submit" value="Sign Up" id="create-account" class="btn btn-success pull-right">
</div>
</form> </template>
Which calls this method in the corresponding file:
signupUser: function(accountData) {
var uid = Accounts.createUser(accountData);
}
So basically what I see here is that your rules don't reflect how the form would be validated. You have an email that must match the email format, but then you have a rule that says it has to be exactly 4 characters long.. a better field definition would look like this:
fields: {
email: {
required: true,
format: 'email',
message: 'Please enter a valid email address'
},
password: {
required: true,
rules: {
minLength: 6,
maxLength: 30,
},
message: "Password must be between 6 and 30 characters"
}
}
I created a github repo that you can clone and run to test this out if you would like.
https://github.com/copleykj/meso-test
Hope this helps.