I am trying out different functionalities using backbone and i came accross a strange one. I am trying to submit a form through backbone. I had done this previously and i cannot find whats wrong with what i am doing.
The code is as follows :
HTML Part
<div clas="loginpage"></div>
<form class="login-user-form">
<input type="text" name="name" id="name" placeholder="Enter Name"><br><br>
<button type="submit" class="btn">Create</button>
</form>
jQuery Part
var UserLogin = Backbone.View.extend({
el:'.loginpage',
initialize:function(){
console.log("Login View Initialized");
},
events:{
'submit .btn' : 'loginuser'
},
loginuser:function(){
console.log("Login Clicked.");
return false;
}
});
var userlogin = new UserLogin();
I get Login View Initialized message in console. But i cannot get the loginuser function to work. The page submits through its default submit functionality.
What am i doing wrong?
1) loginpage doesn't contain the form. Fix:
<div class="loginpage">
<form class="login-user-form">
<input type="text" name="name" id="name" placeholder="Enter Name"><br><br>
<button type="submit" class="btn">Create</button>
</form>
</div>
2)
events : {
'submit' : 'loginuser'
},
loginuser : function(){
console.log("Login Clicked.");
return false; // Stops default html form submission
}
Got it working :
events:{
'submit' : 'loginuser'
}
Got this from the following thread : How do I get backbone to bind the submit event to a form?
Cheers ..:)
Related
I have created an angular form that displays input validation errors received from the server. My solution works fine except for one small issue.
If I submit the form with no value, after the page loads, I am receiving the correct response from my server i.e. 422, but the validation error is not displayed. If I then start typing a value in the input the validation error flashes and disappears.
I am almost certain that it has something to do with my directive, but I'm not sure how to fix it. This is my current directive code:
var appServices = angular.module('webFrontendApp.directives', []);
appServices.directive('serverError', function(){
return {
restrict: 'A',
require: '?ngModel',
link: function(scope,element,attrs,ctrl){
element.on('change keyup', function(){
scope.$apply(function(){
ctrl.$setValidity('server', true);
});
});
}
};
});
I think the issue is with the element.on('change keyup'.... section of this code. That's why the error message flashes when I start typing. Also when I change this to 'change' instead of 'change keyup', the error message is displayed permanently when I start typing.
Does anybody have an idea of how I can display the error message even if I did not type any value into the input before submitting it the first time?
UPDATE AS PER COMMENT
Here is my form:
<form ng-submit="create(memberData)" name="form" novalidate>
<div class = "row form-group" ng-class = "{ 'has-error' : form.email.$dirty && form.email.$invalid }">
<input type="text" ng-model="memberData.email" placeholder="janedoe#mail.com" name="email" class="col-xs-12 form-control" server-error>
<span class="errors" ng-show="form.email.$dirty && form.email.$invalid">
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
<span ng-show="form.email.$error.server">{{errors.email}}</span>
</span>
</div>
<div class="row">
<button type="submit" class="btn btn-danger col-xs-12">Join Private Beta</button>
</div>
</form>
And my controller:
$scope.memberData = {};
$scope.create = function() {
var error, success;
$scope.errors = {};
success = function() {
$scope.memberData = {};
};
error = function(result) {
angular.forEach(result.data.errors, function(errors, field) {
$scope.form[field].$setValidity('server', false);
$scope.errors[field] = errors.join(', ');
});
};
BetaMember.save({ beta_member: { email: $scope.memberData.email || "" }}).$promise.then(success, error);
};
Since the form itself doesn't have a $setValidity method, because doens't have an ng-model, and assuming that the server error is not referred to a single field ( in this case a $setValidity method is preferred ), I think that the simplest solution could be this one:
Create a form with some random validation ( this one simply needs at least the username ) and a div that can display a custom server error.
<div ng-controller="AppCtrl">
<form novalidate name="createForm">
<div class="inputWrap">
<input ng-model="name" name="name" type="text" required placeholder="John Doe">
<span ng-if="createForm.name.$dirty && createForm.name.$invalid">
Some kind of error!
</span>
</div>
<div ng-if="serverError">
{{ serverError.message }}
</div>
<input
value="Join Private Beta"
ng-disabled="createForm.$invalid || serverError"
ng-click="create()"
type="button">
</form>
</div>
Then in your controller you can add the create method that deal with YourService ( should return a promise ) and if the response is failure you can create a simple object with a custom server error message inside which will also be usefull to disable the form button, if you need.
var AppCtrl = function($scope, YourService){
// Properties
// Shared Properties
// Methods
function initCtrl(){}
// Shared Methods
$scope.create = function(){
YourService.makeCall().then(function(response){
// Success!
// Reset the custom error
$scope.serverError = null;
}, function(error){
// Do your http call and then if there's an error
// create the serverError object with a message
$scope.serverError = {
message : 'Some error message'
};
})
};
// Events
// Init controller
initCtrl();
};
AppCtrl.$inject = [
'$scope',
'YourService'
];
app.controller('AppCtrl', AppCtrl);
I mean it's very simple snippet here, I just wanted to bring an example. Nothing to complex, but you can scale this to something more.
It seems that there was an extremely simple workaround. Since I had the filter form.email.$dirty in my view, the error would not be displayed if the user clicked submit without clicking on the form first.
After removing form.email.$dirty and only having form.email.$invalid, it works perfectly. I think this is sufficient in my case, as this validation is dependant on a server response and will not be triggered before the form is submitted. The error object is also cleared up in my controller ensuring that the page does not load an error when it's first loaded.
I would like to call the login function when I click the login button but keep getting the error message in the title. Can someone point out the error in my script?
login.js code below:
/*global Firebase, angular, console*/
'use strict';
// Create a new app with the AngularFire module
var app = angular.module("runsheetApp");
app.controller("AuthCtrl", function ($scope, $firebaseAuth) {
var ref = new Firebase("https://xxxxx.firebaseio.com");
function login() {
ref.authWithPassword({
email : "xxxxx",
password : "xxxx"
}, function (error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
});
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.21/angular.min.js"></script>
And the code for login.html is also below:
<div class="container" style="max-width: 300px">
<form class="form-signin">
<h2 class="form-signin-heading" style="text-align: center">Please Sign In</h2>
<input type="text" class="form-control" name="username" ng-model = "username" placeholder="Email Address" required="" autofocus="" />
</br>
<input type="password" class="form-control" name="password" ng-model = "password" placeholder="Password" required=""/>
</br>
<button class="btn btn-lg btn-primary btn-block" type="submit" ng-click="login()">Login</button>
</form>
</div>
Edge case here, but I want to mention it for posterities' sake. I got this same error when using the controllerAs pattern with a form name with the same value as ng-submit. For example:
<form name="authCtrl.signUp" ng-submit="authCtrl.signUp()">
Throws: TypeError: v2.signUp is not a function
The solution was to change the name of the form to something different:
<form name="authCtrl.signUpForm" ng-submit="authCtrl.signUp()">
In my case, I was having an exact same issue as yours. However, coming across gkalpak's answer to such a scenario helped me out.
Turned out to be what I was calling was addBuddy() function, from a form named "addBuddy". The solution was to change the name of either of the two things to make one stand out or differentiable from the other. I changed the name of the form to "addBuddyForm" and voila! My function worked!
Here's a snippet of my case:
<form name="addBuddy" class="form-horizontal" novalidate>
...
<button class="btn btn-sm btn-info" ng-click="addBuddy()>Submit</button>
Which, I changed to:
<form name="addBuddyForm" class="form-horizontal" novalidate>
...
<button class="btn btn-sm btn-info" ng-click="addBuddy()>Submit</button>
...and it worked! :)
In AngularJS call the function from view it must be in the $scope.
JS
// exposes login function in scope
$scope.login = login;
HTML
<div class="container" ng-controller="AuthCtrl" style="max-width: 300px"> <!-- I notice here for include ng-controller to your main div -->
<form class="form-signin">
<h2 class="form-signin-heading" style="text-align: center">Please Sign In</h2>
<input type="text" class="form-control" name="username" ng-model = "username" placeholder="Email Address" required="" autofocus="" />
</br>
<input type="password" class="form-control" name="password" ng-model = "password" placeholder="Password" required=""/>
</br>
<button class="btn btn-lg btn-primary btn-block" type="submit" ng-click="login()">Login</button>
</form>
This may not be specific to your problem, but I was also getting this error and it took a bit to figure out why.
I had named both a function and a variable the same, with the variable assigned in the function, and so the assignment of the variable was overriding the function and it was exploding on a second run.
You'll notice in the example the uploadFile() function as an upload.uploadFile = true; This was a wonderful file that was meant to be upload.uploadingFile - a flag used to control the behavior of a spinner. Once that was fixed, the issue went away.
Example:
(function()
{
'use strict';
angular.module('aumApp.file-upload')
.controller('FileUploadCtrl', FileUploadCtrl);
function FileUploadCtrl($scope, $http)
{
upload.uploadFile = function()
{
upload.uploadFile = true;
var backendUrl = '/ua_aumcore/events/api/v1/events/uploadFile';
var fd = new FormData();
fd.append('file', upload.src);
$http({ url: backendUrl, data: fd, method: 'POST', transformRequest : angular.identity, headers: { 'Content-Type' : undefined } })
.then(function uploadSuccess(response)
{
upload.data = response.data;
upload.message = "Uploaded Succesfully.";
upload.uploadSuccess = true;
upload.uploadingFile = false;
},
function uploadFailure(response)
{
upload.message = "Upload Failed.";
upload.uploadSuccess = false;
upload.uploadingFile = false;
});
};
}
FileUploadCtrl.$inject = ['$scope', '$http'];
})();
To be callable from the view, a function must be in the $scope. Add
$scope.login = login;
to the JS code of the controller.
You also need to actually use that controller. Change
<div class="container" style="max-width: 300px">
to
<div ng-controller="AuthCtrl" class="container" style="max-width: 300px">
This is all fundamental stuff. My advice would be to learn from an AngularJS tutorial before going further.
Two enable two-way binding you have to assign your login function to $scope. Replace your code for function with this:
$scope.login=function() {
ref.authWithPassword({
email : "nick.koulias#gmail.com",
password : "Jaeger01"
}, function (error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
});
}
It may be a late answer by me.
But It working for me
Check form name you set
e.g. ng-form="login"
and function name
e.g. ng-click="login()"
Then it will not work . You have to change one of them.
e.g. ng-form="loginForm"
Explanation:
AngularJS 1.x registers any form DOM element that has a name property in $scope via formDirectiveFactory. This directive automatically instantiates form.FormController if the above is true:
If the name attribute is specified, the form controller is published onto the current scope under
from: angular.js:24855
Hence if you have a <form name=myForm> it will override your $scope.myForm = function() { ... }
I am trying to add a "hidden" field to a basic form in Angular (using Firebase as the backend). I'm having trouble figuring out how to include this field as part of the array when the form is submitted. I want to include {type: 'Basic'} as part of the array. I've looked at the other related posts on this site, but am still unsure how to apply to my particular situation.
Any suggestions on how to do this?
Javascript:
myApp.controller('NewProjectCtrl', function ($location, Projects) {
var editProject = this;
editProject.type = 'Basic'; //this is the hidden field
editProject.save = function () {
Projects.$add(editProject.project).then(function(data) {
$location.path('/');
});
};
});
HTML:
<form>
<div class="control-group form-group">
<label>Name</label>
<input type="text" name="name" ng-model="editProject.project.name">
</div>
<label>Description</label>
<textarea name="description" class="form-control" ng-model="editProject.project.description"></textarea>
<button ng-click="editProject.save()" class="btn btn-primary">Save</button>
</form>
You don't need a hidden form field, just submit your value in your controller like this:
editProject.save = function () {
editProject.project.type = 'Basic';
Projects.$add(editProject.project).then(function(data) {
$location.path('/');
});
};
All attributes of your editProject.project will be submitted, as you may notice in the developer console.
I would structure the controller a bit different.. here is an example (I am considering you are using angular-resource, where Projects returns a Resource?):
myApp.controller('NewProjectCtrl', function ($location, Projects) {
$scope.project = new Projects({type: 'Basic'});
$scope.save = function () {
$scope.project.$save().then(function(data) {
$location.path('/');
});
};
});
<form ng-submit="save()">
<div class="control-group form-group">
<label>Name</label>
<input type="text" name="name" ng-model="project.name">
</div>
<label>Description</label>
<textarea name="description" class="form-control" ng-model="project.description"></textarea>
<input type="submit" value="Save" class="btn btn-primary" />
</form>
The save function will $save the new project resource (this is an default method and will make a POST on the given resource URL).
I have a view which has the "el" set to the form id. I have two buttons in the form, one for submit and one for clear. I cannot figure out how to set an event for each button. For example when I set the events as follows they will not work:
Form Template:
<form id="addTask" action="">
<input type="text" placeholder="Your new task"/>
<input type="submit" id="submit" value="Add Task"/>
<input type="submit" id="clear" value="Clear" />
</form>
Form View:
App.Views.AddTask = Backbone.View.extend({
el: '#addTask',
events: {
'submit .edit': 'submit',
'submit .clear': 'clear'
},
submit: function(e) {
e.preventDefault();
var newTaskTitle = $(e.currentTarget).find('input[type=text]').val();
var task = new App.Models.Task({ title: newTaskTitle });
this.collection.add(task);
},
clear: function() {
// do some stuff
}
});
When I use the below syntax for the "click" event in another view it works.
events: {
'click .delete': 'destroy',
'click .edit': 'edit'
},
I have Googled and cannot find an answer. Funny thing is I found a tutorial where this syntax is used with a submit event:
http://dailyjs.com/2013/01/31/backbone-tutorial-10/
Any assistance is appreciated. Thanks.
The submit event is triggered on the form itself, in your case your "el", not the clicked button.
events: {
'submit': 'onSubmit'
}
Would work, but you wouldn't be able to know which button has been clicked.
If you have to submit button, I guess the correct way to do it would be to have two forms.
Let me know if that helps!
Edit:
If you don't want to change your form:
Form Template:
<form id="addTask" action="">
<input type="text" placeholder="Your new task"/>
<input type="submit" id="submit" value="Add Task"/>
<input type="submit" id="clear" value="Clear" />
</form>
Form View:
App.Views.AddTask = Backbone.View.extend({
el: '#addTask',
events: {
'submit': 'submit',
'click #clear': 'clear'
},
submit: function(e) {
e.preventDefault();
var newTaskTitle = $(e.currentTarget).find('input[type=text]').val();
var task = new App.Models.Task({ title: newTaskTitle });
this.collection.add(task);
},
clear: function() {
// do some stuff
}
});
Am quite new to AngularJS. The issue is i have a form with two fields- name and profile pic as shown in the code below. I am using ng-upload (https://github.com/twilson63/ngUpload). I want the 'Save' button to work only if either field is dirty and the upload isn't happening currently so that multiple post requests are not triggered on the user clicking on the 'Save' button. But looks like, $dirty works fine with the 'name' field but not with the 'profile pic' field. Am i just missing something? How to go about it keeping it as simple as possible for a beginner of AngularJS. Any help would be appreciated.
//Code
<form id='picUpload' name='picUpload' ng-upload-before-submit="validate()" method='post' data-ng-upload-loading="submittingForm()" action={{getUrl()}} data-ng-upload='responseCallback(content)' enctype="multipart/form-data">
<input type="text" name="name" data-ng-model="user.name" maxlength="15" id="user_screen_name" required>
<input type="file" name="profilePic" data-ng-model="user.profilePic" accept="image/*">
<div class="form-actions">
<button type="submit" class="btn primary-btn" id="settings_save" data-ng-disabled="!(picUpload.name.$dirty|| picUpload.profilePic.$dirty) || formUploading">Save changes</button>
</div>
</form>
//In my JS code
$scope.submittingForm = function(){
$scope.formUploading = true;
}
Regards!
I made a directive ng-file-dirty
.directive('ngFileDirty', function(){
return {
require : '^form',
transclude : true,
link : function($scope, elm, attrs, formCtrl){
elm.on('change', function(){
formCtrl.$setDirty();
$scope.$apply();
});
}
}
})
I haven't used ng-upload before, but you can use onchange event of input element. onchange event is fired whenever user selects a file.
<input type="file" onchange="angular.element(this).scope().fileNameChanged(this)" />
Javascript :
var app = angular.module('MainApp', []);
app.controller('MainCtrl', function($scope)
{
$scope.inputContainsFile = false;
$scope.fileNameChanged = function(element)
{
if(element.files.length > 0)
$scope.inputContainsFile = true;
else
$scope.inputContainsFile = false;
}
});
So now you can check if inputContainsFile variable is true along with dirty check of name field