I have a Model as
var Info = Backbone.Model.extend({
defaults: {
name: '',
email :''
},
initialize: function(){
console.log('Object created');
},
validate: function(attr){
if(!attr.name){
return 'Name cannot be empty';
},
if(!attr.email){
return 'Email cannot be empty';
}
}
});
var model = new Info();
model.set({
name: '',
email: ''
});
var viewClass = Backbone.View.extend({
_modelBind: undefined,
initialize: function(){
this._modelBind = new Backbone.ModelBinder();
this.render();
},
render: function(){
var template = _.template($('#App1').html());
this.$el.html(template);
var bindings = {
name: '[name=name]',
email: '[name=email]'
};
this._modelBind.bind(model, this.el, bindings);
},
'events': {
'click #btnSubmit': 'Submit'
},
Submit: function(){
var response = {
name: model.get('name'),
email: model.get('email')
};
this.model.save(response);
}
});
html is
<script type="text/template" id="App1">
Name: <input type="text" id="name" name="name"/><br />
Email: <input type="text" id="email" name="email" /><br />
<input type="button" id="btnSubmit" value="Submit" />
</script>
On save event, it goes to validate method and after that it goes to the value specified in the url attribute of the model.
I don't want to specify a url parameter. Can i still use validate method without url ?
Call validate directly:
var error = this.model.validate(this.model.attributes);
If you don't specify url parameter, you will get an error on this.model.save():
Uncaught Error: A "url" property or function must be specified
Related
I am implementing a very simple React login page. I have started with the following component, Account.
var Account = React.createClass({
getInitialState: function() {
return {
showSignUp: false,
showLogin: true
}
},
update: function(data) {
this.setState(data);
},
render: function() {
if(this.state.showSignUp) {
return <SignUp/>
}
else {
return <Login update={this.update}/>
}
}
});
As expected, the Login component is displayed and renders the following:
return (
<div>
<p><input type="text" placeholder={Language.languagePack.account.username} onChange={this.usernameChange}/></p>
<p><input type="password" placeholder={Language.languagePack.account.password} onChange={this.passwordChange}/></p>
<p><a onClick={this.performLogin}>{Language.languagePack.account.login}</a></p>
<p><a onClick={this.handleSignUp}>{Language.languagePack.account.signUp}</a></p>
<p>{failedMessage}</p>
</div>
)
This all works fine. The application is picking up on the changes via the onChange hook. If the user clicks "Sign Up" though, then the following code is called:
handleSignUp: function() {
this.props.update({showSignUp: true, showLogin: false})
},
Which calls the update method in the Account class, which updates the state and causes a re-render. This is what causes it to switch to the SignUp component.
return (
<div id="signUp">
<p><input type="text" placeholder={Language.languagePack.account.username} onChange={this.usernameChange} /></p>
<p><input type="password" placeholder={Language.languagePack.account.password} onChange={this.passwordChange} /></p>
<p><input type="email" placeholder={Language.languagePack.account.email} onChange={this.emailChange} /></p>
<p><a onClick={this.handleSignUp}>{Language.languagePack.account.signUp}</a></p>
</div>
)
And for some reason, none of the events are firing on this. onChange or onClick doesn't seem to be registered. I think this is related to my implementation of switching components based on a state change that renders different components. My question is, why is this happening and what part of React have I misunderstood to make this happen?
Full Classes
Login Component
var Login = React.createClass({
getInitialState: function() {
return {
username: '',
password: '',
failed: false
}
},
usernameChange: function(event) {
this.setState({
username: event.target.value,
failed: false
});
},
passwordChange: function(event) {
this.setState({
password: event.target.value,
failed: false
});
},
performLogin: function() {
var username = this.state.username;
var password = this.state.password;
console.log("Attempting login with username " + username + " and password " + password);
var _this = this;
Api.login(username, password, function(response) {
_this.props.update({user: response, loggedIn: true});
},
function(response) {
_this.setState({failed: true});
})
},
handleSignUp: function() {
this.props.update({showSignUp: true, showLogin: false})
},
render: function() {
var failedMessage = null;
if(this.state.failed) {
failedMessage = <div className="failed-auth">{Language.languagePack.account.invalidCredentials}</div>;
}
return (
<div>
<p><input type="text" placeholder={Language.languagePack.account.username} onChange={this.usernameChange}/></p>
<p><input type="password" placeholder={Language.languagePack.account.password} onChange={this.passwordChange}/></p>
<p><a onClick={this.performLogin}>{Language.languagePack.account.login}</a></p>
<p><a onClick={this.handleSignUp}>{Language.languagePack.account.signUp}</a></p>
<p>{failedMessage}</p>
</div>
)
}
});
Signup Component
var SignUp = React.createClass({
getInitialState : function() {
return {
username: '',
password: '',
email: ''
}
},
usernameChange: function(event) {
this.setState({
username: event.target.value
});
},
passwordChange: function(event) {
this.setState({
password: event.target.value
});
},
emailChange: function(event) {
this.setState({
email: event.target.value
});
},
handleSignUp : function() {
var username = this.state.username;
var password = this.state.password;
var email = this.state.email;
console.log("Signing up with username=" + username + " and password=" + password + "and email=" + email);
},
handleLogin : function() {
console.log("Fired!");
},
render: function () {
return (
<div id="signUp">
<p><input type="text" placeholder={Language.languagePack.account.username} onChange={this.usernameChange} /></p>
<p><input type="password" placeholder={Language.languagePack.account.password} onChange={this.passwordChange} /></p>
<p><input type="email" placeholder={Language.languagePack.account.email} onChange={this.emailChange} /></p>
<p><a onClick={this.handleSignUp}>{Language.languagePack.account.signUp}</a></p>
</div>
)
}
});
Your code does work; However, I did remove references to language.LanguagePack, since that's not defined in the code you provided. If you have a javascript error, it will prevent code from running.
https://jsfiddle.net/tqz3skcr/2/
var SignUp = React.createClass({
getInitialState : function() {
return {
username: '',
password: '',
email: ''
}
},
usernameChange: function(event) {
console.log('username Changed');
this.setState({
username: event.target.value
});
},
passwordChange: function(event) {
console.log('password Changed');
this.setState({
password: event.target.value
});
},
emailChange: function(event) {
console.log('email changed');
this.setState({
email: event.target.value
});
},
handleSignUp : function() {
var username = this.state.username;
var password = this.state.password;
var email = this.state.email;
console.log("Signing up with username=" + username + " and password=" + password + "and email=" + email);
},
handleLogin : function() {
console.log("Fired!");
},
render: function () {
return (
<div id="signUp">
<p><input type="text" onChange={this.usernameChange} /></p>
<p><input type="password" onChange={this.passwordChange} /></p>
<p><input type="email" onChange={this.emailChange} /></p>
<p><a onClick={this.handleSignUp}></a></p>
</div>
)
}
});
ReactDOM.render(
<SignUp />,
document.getElementById('container')
);
I don't see anything obvious but you could try this pattern to show/hide the components. Toggle showing and hiding components in ReactJs.
First of all, make your life easier and don't use indicators like:
{
showSignUp: true,
showLogin: false
}
something like this would be much simpler and would produce less errors:
{
formToShow: "signUpForm" // or "loginForm"
}
I would say, if you start coding in this way the issue will resolve by "clean code magic" ))
I have a view created with Backbone.js and inserted into a div element - it shows for about a second and then disappears.
Here is my code for the view:
var addPlayerView = Backbone.View.extend({
tagName: "div",
model: Player,
id: 'addPlayerDiv',
initialize: function() {
console.log('addPlayerView has been created');
},
render: function (){
this.$el.html('<p>show this puppy</p>');
return this;
}
});
here is the model:
var Player = Backbone.Model.extend({
defaults: {
ID: "",
firstName: '',
lastName: ''
},
idAttribute: "ID"
});
and here is the HTML:
<form onsubmit="addNewPlayer();">
<input type="submit" value="Add New Player New"/>
</form>
<p>
<div id="addPlayerDiv"></div>
</p>
<script>
function addNewPlayer() {
var player = new Player({});
var newPlayerView = new addPlayerView({el: $("#addPlayerDiv"), model: player});
newPlayerView.render();
};
</script>
The addNewPlayer() function is being called correctly and the newPlayerView is rendering on the page, but only for a second, then it disappears on the page.
No idea what to do. Anyone have this problem before?
You need cancel the default action (in our case onsubmit tries send data to server)
<form onsubmit="addNewPlayer(); return false;">
or
<form onsubmit="addNewPlayer(event);">
function addNewPlayer(e) {
e.preventDefault();
.....
}
Example
jsfiddle
I'm trying to create a message client using this tutorial at this point, the view is supposed to be updated when a new message is typed into the field and the add button is clicked.
For some reason the "addMessage" Event is failing to add a new model to the collection.
var messagesjson = [
{
id: 3,
message: "This is the message",
sender: "gabriel",
receiver: "gabriel",
has_been_read: false,
has_been_reported: false,
created_at: "2014-10-23T19:55:20+0200",
is_friend: false
},
{
id: 5,
message: "I'm loving this ",
sender: "gabriel",
receiver: "gabriel",
has_been_read: true,
has_been_reported: false,
created_at: "2014-10-23T20:02:34+0200",
is_friend: false
}];
var MessageModel = Backbone.Model.extend({
defaults:
{
id: 3,
message: "This is the message",
sender: "gabriel",
receiver: "gabriel",
has_been_read: false,
has_been_reported: false,
created_at: "2014-10-23T19:55:20+0200",
is_friend: false
}
});
var MessageView = Backbone.View.extend({
tagName: "div",
className: "listview",
template: $('#messageTemplate').html(),
render: function()
{
var tmpl = _.template(this.template);
console.log(this.model);
this.$el.html(tmpl(this.model.toJSON()));
return this;
}
});
var MessageCollection = Backbone.Collection.extend({
model: MessageModel
});
var MessageCollectionView = Backbone.View.extend({
el: $('#messages'),
initialize: function()
{
this.collection = new MessageCollection(messagesjson);
this.render();
this.collection.on("add", this.renderMessage, this);
},
render: function()
{
var that = this;
_.each(this.collection.models, function(item){
that.renderMessage(item);
},this);
},
events:{
"click #add":"addMessage"
},
renderMessage: function(item)
{
var messageview = new MessageView({
model: item
});
this.$el.append(messageview.render().el);
},
addMessage: function(e)
{
e.preventDefault();
var formData = {};
$("#addMessage").children("input").each(function (i, el) {
formData[el.id] = $(el).val();
});
messagesjson.push(formData);
this.collection.add(new MessageModel(formData));
console.log(messagesjson);
}
});
var messagecollectionview = new MessageCollectionView();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<div id="messages">
<form id="addMessage" action="#">
<div>
<label for="messageText">Message: </label>
<input id="messageText" type="text" />
<button id="add">Add</button>
</div>
</form>
</div>
<script type="text/template" id="messageTemplate">
<a href="#" class="list autoWidth <% if(has_been_read) { %> selected <% } %>">
<div class="list-content">
<img src="//www.polyvore.com/cgi/img-thing?.out=jpg&size=l&tid=20774792" class="icon">
<div class="data">
<span class="item-title-secondary fg-gray"><b><%= sender %></b></span>
</div>
<span class="tertiary-text">
<%= message %>
</span>
</div>
</a>
</script>
You are setting the id as 3 for all new models as your defaults hash contains id: 3. The collection thinks it is the same model as it already has a model with that id.
So first you need to change your defaults with id:null:
var MessageModel = Backbone.Model.extend({
defaults:{
id: null,
message: "This is the message",
sender: "gabriel",
receiver: "gabriel",
has_been_read: false,
has_been_reported: false,
created_at: "2014-10-23T19:55:20+0200",
is_friend: false
}
});
Then you need to fix the code getting the formData. First of all you are using jQuery children() method which only looks at immediate childrens. That means you will never get the inputs inside the form as there is an intermediate div. You could use find.
Secondly, you need to make sure that formData has a property named message so it can override the default message: "This is the message". I would add a name attribute message to the input element and use it like formData[el.name] = $(el).val();. (You could later use one of the jquery serializeObject plugins to automatically serialize all input elements this way).
So the addMessage would look like this:
addMessage: function(e){
e.preventDefault();
var formData = {};
$("#addMessage").find("input").each(function (i, el) {
formData[el.name] = $(el).val();
});
messagesjson.push(formData);
this.collection.add(new MessageModel(formData));
}
You can try it in this fiddle
How do I extend properties from a base 'class' in knockout.js?
I'm trying to setup knockout-validation in my view models but it doesn't work unless my 'sub-class' has an observable that is also extended. See the comments in the code snippet below:
// setup knockout validation
ko.validation.rules.pattern.message = 'Invalid.';
ko.validation.configure({
registerExtenders: true,
messagesOnModified: true,
insertMessages: true
});
// base 'class'
function userAddressModelBase(data) {
var self = this;
self.firstName = ko.observable(data.firstName);
self.lastName = ko.observable(data.lastName);
}
userAddressModel.prototype = new userAddressModelBase({});
userAddressModel.constructor = userAddressModel;
// 'sub-class'
function userAddressModel(data) {
var self = this;
userAddressModelBase.call(self, data);
// extend 'base-class' properties; this doesn't work unless the current 'sub-class' also has an observable who is also extended...
self.firstName.extend({ required: true });
self.lastName.extend({ required: true });
// the above statement only starts working if I extend an observable from this `sub-class`
self.city = ko.observable();//.extend({ required: true });
self.errors = ko.validation.group(self);
self.validate = function() {
console.log(self.errors().length);
if (self.errors().length > 0) {
self.errors.showAllMessages();
return;
}
};
}
var model = new userAddressModel({ firstName: "Ted" });
ko.applyBindings(model);
.validationMessage{
color: #ea033d;
font-weight: 700;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div>
<label>First Name: <input type="text" data-bind="value: firstName" /></label><br>
<label>Last Name: <input type="text" data-bind="value: lastName" /></label><br>
<label>City: <input type="text" data-bind="value: city" /></label><br>
<button data-bind="click: validate">Validate</button>
</div>
The recommended way to perform inheritance is to use ko.utils.extend(self, new userAddressModelBase(data));
function userAddressModelBase(data) {
var self = this;
self.firstName = ko.observable(data.firstName);
self.lastName = ko.observable(data.lastName);
}
Below I am using extend to add required validation to firstName and lastName.
function userAddressModel(data) {
var self = this;
ko.utils.extend(self, new userAddressModelBase(data))
self.firstName.extend({ required: true });
self.lastName.extend({ required: true });
};
Now in the example if you remove the firstName and then exit the text box you can see that the required message appears.
http://jsfiddle.net/d4vLqkx2/1/
I want to call a rest service (post) when I press on the button login but it doesn't launch any service it just add a "?" at the end of the url of my application.
here is my js :
(function ($) {
var authentication = Backbone.Model.extend({
defaults: {
Username: "",
Password: ""
},
url:'../../rest/login'
});
var LoginView = Backbone.View.extend({
model: new authentication(),
el: $("#login-form"),
events: {
"click button#login": "login"
},
login: function(){
alert("ici");
this.model.save({username: this.$el.find("#inUser")}, {
password: this.$el.find("#inPswd")}, {
success: function() {
/* update the view now */
},
error: function() {
/* handle the error code here */
}
});
}
})
})
(jQuery);
And here is my form :
<form class="form-inline">
<div class="form-group">
<input type="text" class="form-control" placeholder="Username" id="inUser"></input>
<input type="password" class="form-control" placeholder="Password" id="inPswd"></input>
<button id="login">Login</button>
</div>
</form>
You have a problem with your .save() method call because you send username and password in two different objects.
Also to stop adding question mark ? sign (stop submitting your form) you need to add event.preventDefault(); and/or return false; to your button click handler.
Here is a fix:
login: function(event) {
event.preventDefault();
alert("ici");
this.model.save({
username: this.$el.find("#inUser"),
password: this.$el.find("#inPswd")
}, {
success: function() {
/* update the view now */
},
error: function() {
/* handle the error code here */
}
});
return false;
}