I'm having trouble with a backbone.js app I'm working on as a learning exercise. I set up a jsfiddle for it.
Here's the JavaScript:
var app = {};
$(document).ready(function() {
app.Contact = Backbone.Model.extend({
defaults: {
firstName: '',
lastName: '',
email: ''
},
validate: function(attrs) {
var errors = [];
if (attrs.firstName.trim() == "") {
errors.push({
'message': 'Please enter a first name.',
'field': 'firstName'
});
}
if (attrs.lastName.trim() == "") {
errors.push({
'message': 'Please enter a last name.',
'field': 'lastName'
});
}
if (attrs.email.trim() == "") {
errors.push({
'message': 'Please enter an email address.',
'field': 'email'
});
}
if (errors.length) {
return errors;
} else {
return false;
}
}
});
app.ContactList = Backbone.Collection.extend({
model: app.Contact,
localStorage: new Store('backbone-addressbook')
});
app.contactList = new app.ContactList();
app.ContactView = Backbone.View.extend({
tagName: 'tr',
template: _.template($('#contact-template').html()),
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
initialize: function() {
this.model.on('change', this.render, this);
this.model.on('destroy', this.remove, this);
var self = this;
this.model.on('invalid', function(model, errors) {
_.each(errors, function(error, i) {
console.log(self.el);
$(self.el).find('[data-field="' + error.field + '"]').parent().addClass('has-error');
$(self.el).find('[data-field="' + error.field + '"]').parent().find('.help-block').remove();
$(self.el).find('[data-field="' + error.field + '"]').parent().append('<span class="help-block">' + error.message + '</span>');
});
});
this.model.on('change', function(model, response) {
//console.log(self.el);
$(self.el).removeClass('editing');
this.render;
})
},
events: {
'dblclick label': 'edit',
'keypress .edit': 'updateOnEnter',
'click .destroy': 'destroy',
'click .save': 'close'
},
edit: function(e) {
this.$el.addClass('editing');
$(e.currentTarget).next('input').focus();
},
updateOnEnter: function(e) {
if (e.which == 13) {
this.close(e);
}
},
close: function(e) {
e.preventDefault();
var updateObject = {};
$(this.el).find('input[type="text"]').each(function() {
node = $(this);
updateObject[node.data('field')] = node.val();
});
this.model.save(updateObject, {validate: true});
},
destroy: function() {
this.model.destroy();
}
});
app.AppView = Backbone.View.extend({
el: '#newContact',
initialize: function() {
$(this).find('.has-error').removeClass('has-error');
$(this).remove('.help-block');
app.contactList.on('add', this.addOne, this);
app.contactList.fetch();
var self = this;
app.contactList.on('invalid', function(model, errors) {
_.each(errors, function(error, i) {
console.log(self.el);
$(self.el).find('[data-field="' + error.field + '"]').parent().addClass('has-error');
$(self.el).find('[data-field="' + error.field + '"]').parent().find('.help-block').remove();
$(self.el).find('[data-field="' + error.field + '"]').parent().append('<span class="help-block">' + error.message + '</span>');
});
});
},
events: {
'click .add': 'createContact'
},
createContact: function(e) {
e.preventDefault();
app.contactList.create(this.newAttributes(), {validate: true});
},
addOne: function(contact) {
var view = new app.ContactView({model: contact});
$('#contactList').append(view.render().el);
$('form input[type="text"]').val('');
$('form input[type="text"]').parent().removeClass('has-error');
$('.help-block').remove();
},
newAttributes: function() {
var updateObject = {};
$(this.el).find('input[type="text"]').each(function() {
node = $(this);
updateObject[node.data('field')] = node.val();
});
return updateObject;
},
});
app.appView = new app.AppView();
});
And here's the HTML:
<div class="container">
<section id="addressbookapp">
<header id="header">
<h1>Address Book</h1>
<div class="well">
<form id="newContact" action="#" role="form">
<div class="form-group">
<label class="control-label" for="firstName">First Name</label>
<input data-field="firstName" class="newFirstName form-control input-sm" type="text" />
</div>
<div class="form-group">
<label class="control-label" for="lastName">Last Name</label>
<input data-field="lastName" class="newLastName form-control input-sm" type="text" />
</div>
<div class="form-group">
<label class="control-label" for="email">Email Address</label>
<input data-field="email" class="newEmail form-control input-sm" type="text" />
</div>
<button class="add btn-xs">Add</button>
</form>
</div>
</header>
<section id="main">
<table class="table table-striped">
<caption>Double-click to edit an entry.</caption>
<thead>
<tr>
<th>First</th>
<th>Last</th>
<th>Email</th>
<th></th>
</tr>
</thead>
<tbody id="contactList"></tbody>
</table>
</section>
</section>
</div>
<script id="contact-template" type="text/template">
<form action="#" role="form">
<td>
<label class="control-label" for="firstName"><%- firstName %></label>
<input data-field="firstName" class="firstName input-sm edit" value="<%- firstName %>" type="text" />
</td>
<td>
<label class="control-label" for="lastName"><%- lastName %></label>
<input data-field="lastName" class="lastName input-sm edit" value="<%- lastName %>" type="text" />
</td>
<td>
<label class="control-label" for="email"><%- email %></label>
<input data-field="email" class="email input-sm edit" value="<%- email %>" type="email" />
</td>
<td>
<button class="btn-xs save">Save</button>
<button class="btn-xs destroy">Delete</button>
</td>
</form>
</script>
Specifically, when the user edits an entry in the list (by double-clicking), clears an input (a last name, for example) and then tries to save, there's (correctly) a validation error. The problem is that the form at the top (for creating a new entry) is also responding to the invalid event.
My question is not just how to keep this from happening but what would be the ideal way to organize things. This is a learning exercise for me, so I'd thankful for any tips -- anything you see that could be improved.
It's due to the way you've built the app: in both the "new" and "edit" forms, you tell the app to "display error messages if there's a validation problem in the collection". So when you try editing an existing model and there's a validation problem, the "new" form updates to display the errors.
What you need to do instead, is use a new (blank) model in the "new" form, display errors if it doesn't validate, and add it to the collection if it's valid. That way, both forms have their errors handled by different mechanisms and won't overlap.
See http://jsfiddle.net/n9yq2/3/
app.AppView = Backbone.View.extend({
el: '#newContact',
initialize: function() {
this.model = new app.Contact();
// edited for brevity
this.model.on('invalid', function(model, errors) {
_.each(errors, function(error, i) {
createContact: function(e) {
e.preventDefault();
var attrs = this.newAttributes();
if(this.model.set(attrs, {validate: true})){
app.contactList.create(attrs);
}
},
Related
I am using the Bootstrap framework on my website. Under you can see some js used with semantic, and I want to do something similar in bootstrap.
some of the file: base.js
$.fn.api.settings.api = {
'get_user': '/api/v1/user/{username}',
};
search form in html file:
<div id="check_points" class="ui form">
<form>
<div class="field username">
<input type="text" placeholder="Username">
</div>
<button class="ui submit button">Submit</button>
</form>
<div class="ui response hidden"></div>
</div>
end of html file
<script type="text/javascript">
$('#check_points .submit.button')
.api({
action: 'get_user',
beforeSend: function(settings) {
settings.urlData = {
username: $('#check_points div.field.username input').val()
};
return settings;
},
successTest: function(response) {
return response.error == false || true;
},
onSuccess: function(response) {
var el = $('#check_points .response');
el.addClass('message green');
el.removeClass('red');
el.html('User <strong class="username">' + response.username + '</strong> has <strong class="points">' + response.points + '</strong> points and is ranked <strong class="rank">' + response.rank + '</strong>.');
if (el.hasClass('hidden')) {
el.transition({
animation: 'slide down',
duration: '500ms',
});
}
},
onFailure: function(response) {
var el = $('#check_points .response');
el.addClass('message red');
el.removeClass('green');
el.html('No user with the name <strong class="username">XXX</strong> found.');
el.find('strong.username').text($('#check_points div.field.username input').val());
if (el.hasClass('hidden')) {
el.transition({
animation: 'slide down',
duration: '500ms',
});
}
}
});
</script>
I'm trying to delete the "URL" part in the Discover Meteor Book so that it is more of a chat room. I deleted and changed the post_submit.js so that the error statement return is true. When I goto the "Submit Post" and only submit in the "Text" field and no "URL", it won't submit.
Do I need to change anything in the post_item.js or posts.js in the lib/collections?
Most of this issue seems to be in the Chapter 7 part when the method is introduced.
post_submit.js
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
url: $(e.target).find('[name=url]').val(),
title: $(e.target).find('[name=title]').val()
};
Meteor.call('postInsert', post, function(error, result) {
// display the error to the user and abort
if (error)
return true;
// show this result but route anyway
if (result.postExists)
return true;
Router.go('postsList', {_id: result._id});
});
}
});
post_item.js
Template.postItem.helpers({
ownPost: function() {
return this.userId === Meteor.userId();
},
domain: function() {
var a = document.createElement('a');
a.href = this.url;
return a.hostname;
}
});
Does this.url do anyting?
posts.js
Posts = new Mongo.Collection('posts');
Meteor.methods({
postInsert: function(postAttributes) {
check(this.userId, String);
check(postAttributes, {
title: String,
url: String
});
var postWithSameLink = Posts.findOne({url: postAttributes.url});
if (postWithSameLink) {
return {
postExists: true,
_id: postWithSameLink._id
}
}
var user = Meteor.user();
var post = _.extend(postAttributes, {
userId: user._id,
author: user.username,
submitted: new Date()
});
var postId = Posts.insert(post);
return {
_id: postId
};
}
});
url is in here too..
post_submit.html
<template name="postSubmit">
<form class="main form page">
<div class="form-group">
<label class="control-label" for="title">Title</label>
<div class="controls">
<input name="title" id="title" type="text" value="" placeholder="Name your post" class="form-control"/>
</div>
</div>
<input type="submit" value="Submit" class="btn btn-primary"/>
</form>
</template>
You should remove all references to the url if you don't want it.
Check out the project: git clone https://github.com/DiscoverMeteor/Microscope.git
Look for all files containing "url": grep -i -r url *
Results:
client/templates/posts/post_item.html: <h3>{{title}}<span>{{domain}}</span></h3>
client/templates/posts/post_submit.js: url: $(e.target).find('[name=url]').val(),
client/templates/posts/post_submit.js: if (errors.title || errors.url)
client/templates/posts/post_submit.html: <div class="form-group {{errorClass 'url'}}">
client/templates/posts/post_submit.html: <label class="control-label" for="url">URL</label>
client/templates/posts/post_submit.html: <input name="url" id="url" type="text" value="" placeholder="Your URL" class="form-control"/>
client/templates/posts/post_submit.html: <span class="help-block">{{errorMessage 'url'}}</span>
client/templates/posts/post_edit.js: url: $(e.target).find('[name=url]').val(),
client/templates/posts/post_edit.js: if (errors.title || errors.url)
client/templates/posts/post_edit.html: <div class="form-group {{errorClass 'url'}}">
client/templates/posts/post_edit.html: <label class="control-label" for="url">URL</label>
client/templates/posts/post_edit.html: <input name="url" id="url" type="text" value="{{url}}" placeholder="Your URL" class="form-control"/>
client/templates/posts/post_edit.html: <span class="help-block">{{errorMessage 'url'}}</span>
client/templates/posts/post_item.js: a.href = this.url;
lib/collections/posts.js: return (_.without(fieldNames, 'url', 'title').length > 0);
lib/collections/posts.js: return errors.title || errors.url;
lib/collections/posts.js: if (!post.url)
lib/collections/posts.js: errors.url = "Please fill in a URL";
lib/collections/posts.js: url: String
lib/collections/posts.js: if (errors.title || errors.url)
lib/collections/posts.js: throw new Meteor.Error('invalid-post', "You must set a title and URL for your post");
lib/collections/posts.js: var postWithSameLink = Posts.findOne({url: postAttributes.url});
server/fixtures.js: url: 'http://sachagreif.com/introducing-telescope/',
server/fixtures.js: url: 'http://meteor.com',
server/fixtures.js: url: 'http://themeteorbook.com',
server/fixtures.js: url: 'http://google.com/?q=test-' + i,
Those are the files the need to be modified.
post_item.html:
<template name="postItem">
<div class="post">
upvote
<div class="post-content">
<h3>{{title}}</h3>
<p>
{{pluralize votes "Vote"}},
submitted by {{author}},
{{pluralize commentsCount "comment"}}
{{#if ownPost}}Edit{{/if}}
</p>
</div>
Discuss
</div>
</template>
post_submit.js:
Template.postSubmit.onCreated(function() {
Session.set('postSubmitErrors', {});
});
Template.postSubmit.helpers({
errorMessage: function(field) {
return Session.get('postSubmitErrors')[field];
},
errorClass: function (field) {
return !!Session.get('postSubmitErrors')[field] ? 'has-error' : '';
}
});
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
title: $(e.target).find('[name=title]').val()
};
var errors = validatePost(post);
if (errors.title)
return Session.set('postSubmitErrors', errors);
Meteor.call('postInsert', post, function(error, result) {
// display the error to the user and abort
if (error)
return throwError(error.reason);
Router.go('postPage', {_id: result._id});
});
}
});
post_submit.html:
<template name="postSubmit">
<form class="main form page">
<div class="form-group {{errorClass 'title'}}">
<label class="control-label" for="title">Title</label>
<div class="controls">
<input name="title" id="title" type="text" value="" placeholder="Name your post" class="form-control"/>
<span class="help-block">{{errorMessage 'title'}}</span>
</div>
</div>
<input type="submit" value="Submit" class="btn btn-primary"/>
</form>
</template>
post_edit.js:
Template.postEdit.onCreated(function() {
Session.set('postEditErrors', {});
});
Template.postEdit.helpers({
errorMessage: function(field) {
return Session.get('postEditErrors')[field];
},
errorClass: function (field) {
return !!Session.get('postEditErrors')[field] ? 'has-error' : '';
}
});
Template.postEdit.events({
'submit form': function(e) {
e.preventDefault();
var currentPostId = this._id;
var postProperties = {
title: $(e.target).find('[name=title]').val()
}
var errors = validatePost(postProperties);
if (errors.title)
return Session.set('postEditErrors', errors);
Posts.update(currentPostId, {$set: postProperties}, function(error) {
if (error) {
// display the error to the user
throwError(error.reason);
} else {
Router.go('postPage', {_id: currentPostId});
}
});
},
'click .delete': function(e) {
e.preventDefault();
if (confirm("Delete this post?")) {
var currentPostId = this._id;
Posts.remove(currentPostId);
Router.go('home');
}
}
});
post_edit.html:
<template name="postEdit">
<form class="main form page">
<div class="form-group {{errorClass 'title'}}">
<label class="control-label" for="title">Title</label>
<div class="controls">
<input name="title" id="title" type="text" value="{{title}}" placeholder="Name your post" class="form-control"/>
<span class="help-block">{{errorMessage 'title'}}</span>
</div>
</div>
<input type="submit" value="Submit" class="btn btn-primary submit"/>
<hr/>
<a class="btn btn-danger delete" href="#">Delete post</a>
</form>
</template>
post_item.js:
Template.postItem.helpers({
ownPost: function() {
return this.userId == Meteor.userId();
},
upvotedClass: function() {
var userId = Meteor.userId();
if (userId && !_.include(this.upvoters, userId)) {
return 'btn-primary upvotable';
} else {
return 'disabled';
}
}
});
Template.postItem.events({
'click .upvotable': function(e) {
e.preventDefault();
Meteor.call('upvote', this._id);
}
});
posts.js:
Posts = new Mongo.Collection('posts');
Posts.allow({
update: function(userId, post) { return ownsDocument(userId, post); },
remove: function(userId, post) { return ownsDocument(userId, post); },
});
Posts.deny({
update: function(userId, post, fieldNames) {
// may only edit the following fields:
return (_.without(fieldNames, 'title').length > 0);
}
});
Posts.deny({
update: function(userId, post, fieldNames, modifier) {
var errors = validatePost(modifier.$set);
return errors.title;
}
});
validatePost = function (post) {
var errors = {};
if (!post.title)
errors.title = "Please fill in a headline";
return errors;
}
Meteor.methods({
postInsert: function(postAttributes) {
check(this.userId, String);
check(postAttributes, {
title: String
});
var errors = validatePost(postAttributes);
if (errors.title)
throw new Meteor.Error('invalid-post', "You must set a title for your post");
var user = Meteor.user();
var post = _.extend(postAttributes, {
userId: user._id,
author: user.username,
submitted: new Date(),
commentsCount: 0,
upvoters: [],
votes: 0
});
var postId = Posts.insert(post);
return {
_id: postId
};
},
upvote: function(postId) {
check(this.userId, String);
check(postId, String);
var affected = Posts.update({
_id: postId,
upvoters: {$ne: this.userId}
}, {
$addToSet: {upvoters: this.userId},
$inc: {votes: 1}
});
if (! affected)
throw new Meteor.Error('invalid', "You weren't able to upvote that post");
}
});
fixtures.js:
// Fixture data
if (Posts.find().count() === 0) {
var now = new Date().getTime();
// create two users
var tomId = Meteor.users.insert({
profile: { name: 'Tom Coleman' }
});
var tom = Meteor.users.findOne(tomId);
var sachaId = Meteor.users.insert({
profile: { name: 'Sacha Greif' }
});
var sacha = Meteor.users.findOne(sachaId);
var telescopeId = Posts.insert({
title: 'Introducing Telescope',
userId: sacha._id,
author: sacha.profile.name,
submitted: new Date(now - 7 * 3600 * 1000),
commentsCount: 2,
upvoters: [], votes: 0
});
Comments.insert({
postId: telescopeId,
userId: tom._id,
author: tom.profile.name,
submitted: new Date(now - 5 * 3600 * 1000),
body: 'Interesting project Sacha, can I get involved?'
});
Comments.insert({
postId: telescopeId,
userId: sacha._id,
author: sacha.profile.name,
submitted: new Date(now - 3 * 3600 * 1000),
body: 'You sure can Tom!'
});
Posts.insert({
title: 'Meteor',
userId: tom._id,
author: tom.profile.name,
submitted: new Date(now - 10 * 3600 * 1000),
commentsCount: 0,
upvoters: [], votes: 0
});
Posts.insert({
title: 'The Meteor Book',
userId: tom._id,
author: tom.profile.name,
submitted: new Date(now - 12 * 3600 * 1000),
commentsCount: 0,
upvoters: [], votes: 0
});
for (var i = 0; i < 10; i++) {
Posts.insert({
title: 'Test post #' + i,
author: sacha.profile.name,
userId: sacha._id,
submitted: new Date(now - i * 3600 * 1000 + 1),
commentsCount: 0,
upvoters: [], votes: 0
});
}
}
So I am using Knockout Validation to validate my viewmodel and a custom knockout datepicker bindingHandler to attach a jQuery-UI datepicker to dynamically added items in my observableArray.
It seems my bindingHandler is destroying or breaking the validation rules on that field. Neither of the validation rules for the Start or End date seem to be getting enforced.
JSFiddle Link of my code
HTML code:
<form>
<a class="btn btn-default" data-bind="click: function () { $root.addMed(); }">Add New Medication</a>
<h6 data-bind="visible: patientMeds().length < 1">No medications entered.</h6>
<table class="table table-condensed" data-bind="visible: patientMeds().length > 0">
<thead>
<tr>
<th>Med</th>
<th>From</th>
<th>To</th>
<th></th>
</tr>
</thead>
<tbody data-bind="foreach: patientMeds">
<tr>
<td>
<input class="form-control" data-bind="value: MedicationID" />
</td>
<td>
<input class="form-control" data-bind="datepicker: StartDate, datepickerOptions: { changeMonth: true, changeYear: true, showButtonPanel: true }" />
</td>
<td>
<input class="form-control" data-bind="datepicker: DiscontinuedDate, datepickerOptions: { changeMonth: true, changeYear: true, showButtonPanel: true }" />
</td>
<td>
<button class="btn btn-default" data-bind="click: $parent.removeMed">Delete</button>
</td>
</tr>
</tbody>
</table>
</form>
Javascript / ViewModel code:
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options);
//handle the field changing
ko.utils.registerEventHandler(element, "change", function () {
var observable = valueAccessor();
observable($(element).datepicker("getDate"));
});
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
$(element).datepicker("destroy");
});
}
};
function PatientMedication(p) {
var self = this;
p = p || {};
self.MedicationID = ko.observable(p.MedicationID || 1)
.extend({
required: {
params: true,
message: 'Please enter a medication'
},
number: true
});
self.StartDate = ko.observable(p.StartDate).extend({
required: {
params: true,
message: 'Please enter a date'
},
date: true
});
self.DiscontinuedDate = ko.observable(p.DiscontinuedDate || '').extend({
required: {
params: true,
message: 'Please enter a date'
},
date: true
});
}
function MedicationViewModel() {
var self = this;
self.patientMeds = ko.observableArray([]);
self.addMed = function () {
self.patientMeds.unshift(new PatientMedication());
};
self.removeMed = function (med) {
self.patientMeds.remove(med)
};
};
var medvm = new MedicationViewModel();
ko.applyBindings(medvm);
The validation plugin only hooks into the value, checked, textinput and selectedOptions bindings.
If you want to make your custom binding trigger the validations you need to call the validation.makeBindingHandlerValidatable method of the plugin and pass in the name of your custom binding:
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//...
}
};
ko.validation.makeBindingHandlerValidatable('datepicker');
Demo JSFiddle.
I am new to the MEAN and i am trying to make a simple CRUD application. I am getting an error of undefined on my _id and i do not understand why. This variable works withevery other function I call it in. Hopefully someone can help. I am getting the error on line 117 in my controller.js file
Here is the controller.js code for my application
todoApp.controller('TodoCtrl', function($rootScope, $scope, todosFactory) {
$scope.todos = [];
$scope.isEditable = [];
// get all Todos on Load
todosFactory.getTodos().then(function(data) {
$scope.todos = data.data;
});
// Save a Todo to the server
$scope.save = function($event) {
if ($event.which == 13 && $scope.todoInput) {
todosFactory.saveTodo({
"todo": $scope.todoInput,
"isCompleted": false
}).then(function(data) {
$scope.todos.push(data.data);
});
$scope.todoInput = '';
}
};
//update the status of the Todo
$scope.updateStatus = function($event, _id, i) {
var cbk = $event.target.checked;
var _t = $scope.todos[i];
todosFactory.updateTodo({
_id: _id,
isCompleted: cbk,
todo: _t.todo
}).then(function(data) {
if (data.data.updatedExisting) {
_t.isCompleted = cbk;
} else {
alert('Oops something went wrong!');
}
});
};
// Update the edited Todo
$scope.edit = function($event, i) {
if ($event.which == 13 && $event.target.value.trim()) {
var _t = $scope.todos[i];
todosFactory.updateTodo({
_id: _t._id,
todo: $event.target.value.trim(),
isCompleted: _t.isCompleted
}).then(function(data) {
if (data.data.updatedExisting) {
_t.todo = $event.target.value.trim();
$scope.isEditable[i] = false;
} else {
alert('Oops something went wrong!');
}
});
}
};
// Delete a Todo
$scope.delete = function(i) {
todosFactory.deleteTodo($scope.todos[i]._id).then(function(data) {
if (data.data) {
$scope.todos.splice(i, 1);
}
});
};
});
todoApp.controller('TodoCtrl', function($rootScope, $scope, todosFactory) {
$scope.todos = [];
$scope.isEditable = [];
// get all Todos on Load
todosFactory.getTodos().then(function(data) {
$scope.todos = data.data;
});
// Save a Todo to the server
$scope.save = function($event) {
if ($event.which == 13 && $scope.todoInput) {
todosFactory.saveTodo({
"todo": $scope.todoInput,
"isCompleted": false
}).then(function(data) {
$scope.todos.push(data.data);
});
$scope.todoInput = '';
}
};
//update the status of the Todo
$scope.updateStatus = function($event, _id, i) {
var cbk = $event.target.checked;
var _t = $scope.todos[i];
todosFactory.updateTodo({
_id: _id,
isCompleted: cbk,
todo: _t.todo
}).then(function(data) {
if (data.data.updatedExisting) {
_t.isCompleted = cbk;
} else {
alert('Oops something went wrong!');
}
});
};
// Update the edited Todo
$scope.edit = function($event, i) {
if ($event.which == 13 && $event.target.value.trim()) {
var _t = $scope.todos[i];
todosFactory.updateTodo({
_id: _t._id,
todo: $event.target.value.trim(),
isCompleted: _t.isCompleted
}).then(function(data) {
if (data.data.updatedExisting) {
_t.todo = $event.target.value.trim();
$scope.isEditable[i] = false;
} else {
alert('Oops something went wrong!');
}
});
}
};
// Delete a Todo
$scope.delete = function(i) {
todosFactory.deleteTodo($scope.todos[i]._id).then(function(data) {
if (data.data) {
$scope.todos.splice(i, 1);
}
});
};
});
Just is case the error is in either my factory.js code or html, I will include both.
Here is the factory.js code:
todoApp.factory('todosFactory', function($http){
var urlBase = '/api/todos';
var _todoService = {};
_todoService.getTodos = function(){
return $http.get(urlBase);
};
_todoService.saveTodo = function(todo){
return $http.post(urlBase, todo);
};
_todoService.updateTodo = function(todo) {
return $http.put(urlBase, todo);
};
_todoService.deleteTodo = function(id){
return $http.delete(urlBase + '/' + id);
};
return _todoService;
});
Here the html partial that uses the controller and factory:
<div class="container" ng-controller="TodoCtrl">
<div class="row col-md-12">
<div>
<input type="text" class="form-control input-lg" placeholder="Enter a todo" ng-keypress="save($event)" ng-model="todoInput">
</div>
</div>
<div class="row col-md-12 todos">
<div class="alert alert-info text-center" ng-hide="todos.length > 0">
<h3>Nothing Yet!</h3>
</div>
<div ng-repeat="todo in todos" class=" col-md-12 col-sm-12 col-xs-12" ng-class="todo.isCompleted ? 'strike' : ''">
<div class="col-md-1 col-sm-1 col-xs-1">
<input type="checkbox" ng-checked="todo.isCompleted" ng-click="updateStatus($event, todo._id, $index)">
</div>
<div class="col-md-8 col-sm-8 col-xs-8">
<span ng-show="!isEditable[$index]">{{todo.todo}}</span>
<input ng-show="isEditable[$index]" type="text" value="{{todo.todo}}" ng-keypress="edit($event)">
<input ng-show="isEditable[$index]" type="button" class="btn btn-warning" value="Cancel" ng-click="isEditable[$index] = false" />
</div>
<div class="col-md-3 col-sm-3 col-xs-3" >
<input type="button" class="btn btn-info" ng-disabled="todo.isCompleted" class="pull-right" value="edit" ng-click="isEditable[$index] = true" />
<input type="button" class="btn btn-danger" class="pull-right" value="Delete" ng- click="delete($index)" />
</div>
</div>
</div>
This line must be the cause of the issue:
<input ng-show="isEditable[$index]" type="text" value="{{todo.todo}}"
ng-keypress="edit($event)">
You forgot to pass the $index as the second parameter of the edit function. This should fix it:
<input ng-show="isEditable[$index]" type="text" value="{{todo.todo}}"
ng-keypress="edit($event, $index)">
Just getting started with Parse.com and following their JavaScript ToDo app tutorial and having some issues.
Basically it will work perfectly and post to the Parse database when I just have the Username and Password but if I try to add any additional fields like email or phone, it won't send it to the database. I have already defined both email and phone fields in my Parse database.
Any help would be appreciated. Thanks!
My html for my form:
<script type="text/template" id="login-template">
<header id="header"></header>
<form class="signup-form">
<h2>Sign Up</h2>
<div class="error" style="display:none"></div>
<input type="text" id="signup-username" placeholder="Username" />
<input type="password" id="signup-password" placeholder="Create a Password" />
<input type="email" id="signup-email" placeholder="Email" />
<input type="text" id="signup-phone" placeholder="Phone" />
<button>Sign Up</button>
</form>
</div>
</script>
My JS:
var LogInView = Parse.View.extend({
events: {
"submit form.signup-form": "signUp"
},
el: ".content",
initialize: function() {
_.bindAll(this, "signUp");
this.render();
},
signUp: function(e) {
var self = this;
var username = this.$("#signup-username").val();
var password = this.$("#signup-password").val();
var email = this.$("#signup-email").val();
var phone = this.$("#signup-phone").val();
Parse.User.signUp(username, password, email, phone, { ACL: new Parse.ACL() }, {
success: function(user) {
new ManageTodosView();
self.undelegateEvents();
delete self;
},
error: function(user, error) {
self.$(".signup-form .error").html(error.message).show();
self.$(".signup-form button").removeAttr("disabled");
}
});
this.$(".signup-form button").attr("disabled", "disabled");
return false;
},
render: function() {
this.$el.html(_.template($("#login-template").html()));
this.delegateEvents();
}
});
You should use the following syntax, like specified in the javascript docs (https://parse.com/docs/js_guide):
var user = new Parse.User();
user.set("username",username);
user.set("password",password);
user.set("email",email);
user.set("phone",phone);
user.signUp(null,{
success:function(user){...},
error:function(user,error){...}
});
You can add any fields you want with the set method.
I had to change <!--<button>Submit</button>--> to <!--<input type="submit"></input>--> to get this to work but then it did. Thanks.