Backbone - How to this.model.save() an attribute with array? - arrays

I have this model:
var Contact = Backbone.Model.extend({
defaults: function () {
return {
idc: ""
name: ""
email: "",
phones: new Array()
}
},
urlRoot: 'admin/contact'
});
This form (underscore template):
<form>
<input type="hidden" value="{{ idc }}" />
<p>
<label>Name:</label>
<input type="text" name="name" value="{{ name}}" />
</p>
<p>
<label>Email:</label>
<input type="text" name="email" value="{{ email }}" />
</p>
<p>
<label>Phones:</label>
<input type="text" name="phones[]" value="" />
<input type="text" name="phones[]" value="" />
<input type="text" name="phones[]" value="" />
</p>
<button class="cancel">Cancel</button><button class="save">Save</button>
</form>
And when I click in save, this function into Contact View:
e.preventDefault();
var formData = {},
prev = this.model.previousAttributes();
$(e.target).closest("form").find(":input").each(function () {
var el = $(this);
formData[el.attr("name")] = el.val();
});
this.model.set(formData);
this.model.save();
this.render();
I expect that when the function is called, it creates an array to send for the PHP file with the phones, so I can store it in the database. But looking at the browser console the attribute array is with the value: []
Is there something I´m missing to pass it to the PHP file?
PS: The array is in the formData array, but when I call this.model.save() looks like it lost the array.
Anyway, Thankyou!

Your problem is basically that you're using jQuery's serializeArray. serializeArray is great if you want to create a model for every form element, because it produces JSON like this (example JSON taken from the jQuery documentation):
[
{name: 'firstname', value: 'Hello'},
{name: 'lastname', value: 'World'},
{name: 'alias'}, // this one was empty
]
However, you don't want that, you want to create a single model from the combined elements; in other words you want:
{
firstname: 'Hello',
lastname: 'World',
alias: undefined
}
There are jQuery plug-ins that produce output like that if you want, but personally I'd recommend just writing your own. If you have a form view with an onSubmit handler, you could do something like:
this.model.set('name', this.$('[name="name"]').val());
this.model.set('email', this.$('[name="email"]').val());
// etc.
that's tailored specifically for your app and serializes the data the exact way you want.

Related

Insert into JSON array from HTML form

I have an HTML form with the following fields: Note Type (as a combobox), Note Author: (as a textbox), and Note Description (as a textarea). This HTML form is written inside an AngularJS service in a variable and then displayed.
On click of the Save Note button, I need this information to be sent to a JSON array stored in another JavaScript file, which in turn, is displayed on a table as a list of notes.
The JSON array must be in the following form after say, two notes are added:
var noteData = [
{
"NoteType": "Type1",
"NoteAuthor": "John Doe",
"NoteDescription": "My first note."
},
{
"NoteType": "Type2",
"NoteAuthor": "Peter Doe",
"NoteDescription": "My second note."
} ];
How do I go about doing this?
My HTML form inside the AngularJS service looks like this:
var addNoteSection = '<label>Note Type: </label><br />' +
'<select ng-model="myModel.NoteType"><option value="Type 1">Type 1</option><option value="Type 2">Type 2</option>' +
'<label>Note Author: </label><br /><input type="text" ng-model="myModel.NoteCreator" /><br />' +
'<label>Note Description: </label><br /><textarea ng-model="myModel.NoteDescription" rows="4" cols="50"></textarea><br />' +
'<button ng-click="saveNewNote()">Save Note</button>'
try this.
var app = angular.module("app",[]);
app.controller("MyCtrl" , function($scope){
$scope.noteData=[];
$scope.myModel = {
"NoteType": "",
"NoteCreator":"",
"NoteDescription":""
};
$scope.saveNewNote = function(model){
var item = {
"NoteType": model.NoteType,
"NoteCreator": model.NoteCreator,
"NoteDescription": model.NoteDescription
};
$scope.noteData.push(item);
console.log($scope.noteData);
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MyCtrl">
<label>Note Type: </label><br />
<select ng-model="myModel.NoteType">
<option value="Type 1">Type 1</option>
<option value="Type 2">Type2</option>
</select>
<label>Note Author: </label><br />
<input type="text" ng-model="myModel.NoteCreator" /><br />
<label>Note Description: </label><br />
<textarea ng-model="myModel.NoteDescription" rows="4" cols="50"></textarea><br />
<button ng-click="saveNewNote(myModel)">Save Note</button>
</div>
$scope.saveNewNote = function() { noteData.push($scope.myModel); }

How to have an object with an array of objects from form in AngularJS

I'm trying to create a 'recipe' object that contains an array of 'ingredients' and their properties when a form is submitted. I created inputs with ng-models: recipe.name, recipe.servings, recipe.ingredients[0].name, recipe.ingredients[0].amount, recipe.ingredients[1].name, recipe.ingredients[1].amount, etc. But when the form is submitted, only recipe.ingredients[0]'s properties are recorded. In my controller, I have the following:
angular.module('recipeMavenApp')
.controller('AddRecipeCtrl', function () {
var vm = this;
vm.ingredientCount = 1;
vm.recipe = {
name: '',
servings: 0,
ingredients: [],
};
vm.appendIngredient = function() {
var newIngredientInput = angular.element('<input type="text"' +
'ng-model="recipe.ingredients[' + vm.ingredientCount + '].name" placeholder="Ingredient name" />' +
'<input type="number" min="0.25" max="1000" step="0.25" ng-model="recipe.ingredients[' +
vm.ingredientCount + '].amount" placeholder="Amount"/>');
angular.element(document.querySelector('#ingredients')).append(newIngredientInput);
vm.ingredientCount++;
};
vm.addRecipe = function(recipe) {
vm.recipe = recipe;
console.log(vm.recipe); //Testing to see what is received.
};
The form:
<form novalidate >
<div class="form-group">
<label for="recipeName">Name of Recipe</label>
<input type="text" ng-model="recipe.name" id="recipeName" required/>
</div>
<div class="form-group">
<label for="recipeServings">Number of Servings</label>
<input type="number" min="1" max="50" ng-model="recipe.servings" id="recipeServings"/>
</div>
<div class="form-group" id="ingredients">
<label for="recipeIngredients">Ingredients</label>
<button class="btn btn-primary btn-xs" ng-click="add.appendIngredient()">Add Ingredient</button>
<br />
<input type="text" ng-model="recipe.ingredients[0].name" id="recipeIngredients" placeholder="Ingredient name" />
<input type="number" min="0.25" max="1000" step="0.25" ng-model="recipe.ingredients[0].amount" placeholder="Amount"/>
<br/>
</div>
<button ng-click="add.addRecipe(recipe)" class="btn btn-primary"><span class="glyphicon glyphicon-share"></span> Add Recipe</button>
</form>
How do I capture all ingredients in the recipe.ingredients array on form submit?
I tried to rewrite your code there : JSFiddle
I used ng-repeat to generate the list of ingredients (where I uses the $index for the models) to avoid any DOM manipulation in the controller :
<div ng-repeat="ingredient in recipe.ingredients">
<input type="text" ng-model="recipe.ingredients[$index].name" placeholder="Ingredient name" />
<input type="number" min="0.25" max="1000" step="0.25" ng-model="recipe.ingredients[$index].amount" placeholder="0"/>
</div>
Based on the model :
$scope.recipe = {
name: '',
servings: 0,
ingredients: [{
name: '',
amount: null
}]
};
In the $scope.recipe.ingredients you can add how many {name:'', amount:null} as you need to show by default (you can also add a prefilled name or amount, for instance : {name:'Ingredient 1', amount:5}).
Then when I need a new ingredient I just push a new object in the $scope.ingredients array :
$scope.appendIngredient = function() {
$scope.recipe.ingredients.push({
name: '',
amount: null
});
};
Feel free to let me know if it fulfills your requirements or if you have any question.
Thanks

Repeating a Form in Angular

I've got a form that allows users to create a service. Currently you can only add one provider to that service.
I'd like to allow users to add up to 10 providers, using the "Add Another Provider" button.
Here's my code at the moment:
add-service.html
<form role="form" name="createServiceForm">
<input type="text" ng-model="title">
<h2>Attach Provider</h2>
<input type="text" ng-model="provider.title">
<textarea rows="3" ng-model="provider.description"></textarea>
<button type="submit">Add Another Provider</button>
<button type="submit" ng-click="createService()">Submit</button>
</form>
main.js
$scope.createService = function() {
var newService = {
title: $scope.title,
providers: {
provider: {
title: $scope.provider.title,
description: $scope.provider.description
}
},
};
var promise = ServiceService.add(newService);
};
I could duplicate parts of the code like so:
<input type="text"ng-model="provider1.title">
<input type="text"ng-model="provider2.title">
<input type="text"ng-model="provider3.title">
...
providers: {
provider1: {
title: $scope.provider1.title,
},
provider2: {
title: $scope.provider2.title,
},
provider3: {
title: $scope.provider3.title,
}
...
}
But that seems like a messy solution...
What's the best way to duplicate the provider portion of the form, when you click "Add Another Provider" without repeating it 10 times in my HTML and in my newService object?
You can accomplish what you want by using ng-repeat. I've made providers into an array, which you can then iterate over. You could use ng-repeat with an object if the key is important to you.
Also added a function which will push a new provider onto the array, which will then show in the form.
main.js
$scope.providers = [];
$scope.addProvider = function() {
$scope.providers.push({
title: '',
description: ''
});
};
// Start with one provider
$scope.addProvider();
$scope.createService = function() {
var newService = {
title: $scope.title,
providers: $scope.providers,
};
var promise = ServiceService.add(newService);
};
addService.html
<form role="form" name="createServiceForm">
<input type="text" ng-model="title">
<h2>Attach Provider</h2>
<div ng-repeat="provider in providers">
<input type="text" ng-model="provider.title">
<textarea rows="3" ng-model="provider.description"></textarea>
</div>
<button ng-click="addProvider()">Add Another Provider</button>
<button type="submit" ng-click="createService()">Submit</button>
</form>

Angular way to collect values from several inputs

I have following trouble. I have several rows with
dynamically generated inputs in AngularJS view. I'm searching
elegant way to get array from this generated inputs.
This is me html:
<div ng-app>
<div ng-controller="TestCtrl">
<input type="button" value="+" ng-click="addNewRow();"/>
<div ng-repeat="item in items"><input type="text" name="key" ng-value="{item.name}"/> : <input type="text" ng-value="{item.value}"/>
<input type="button" value="x" ng-click="removeItem($index);"/>
</div>
<input type="button" value="Test" ng-click="showItems();"/>
</div>
</div>
and this is my javascript code:
function TestCtrl($scope) {
$scope.items = [
{name: "", value: ""}
];
$scope.addNewRow = function () {
$scope.items.push({
name: "",
value: ""
});
};
$scope.removeItem = function (index) {
$scope.items.splice(index,1);
};
$scope.showItems = function() {
alert($scope.items.toSource());
}
};
alert($scope.items.toSource()); will work correct only under Firefox and as you can
see array is empty. I'm searching a way to update array or other angular way
method.
document.querySelector("input[attr]") or jQuery similar is not good idea I think.
Here is working jsFiddle:
http://jsfiddle.net/zono/RCW2k/21/
I would appreciate any advice and ideas.
Best regards.
Use ngModel:
The ngModel directive binds an input,select, textarea (or custom form
control) to a property on the scope using NgModelController, which is
created and exposed by this directive.
Your view should look like:
<div ng-repeat="item in items">
<input type="text" ng-model="item.name"/> :
<input type="text" ng-model="item.value"/>
<input type="button" value="x" ng-click="removeItem($index);"/>
</div>
(As for the use of toSource() in your code, it is not part of any standard - Gecko-only)
Working example: http://jsfiddle.net/rgF37/

Display button only on form input change in angular.js

HTML:
<ul>
<li><a><i class="icon-white icon-save"></i></a></li>
</ul>
<form>
<input type="text" value="{{ selectedUser.firstname }}" ng-model="selectedUser.firstname">
<input type="text" value="{{ selectedUser.lastname }}" ng-model="selectedUser.lastname">
</form>
I am dealing with user objects fetched from my REST API. So basically there is a list of users. On click the above form is revealed.
function UserController($scope, User){
$scope.users = User.query();
$scope.selectedUser = null;
$scope.select = function(user){
$scope.selectedUser = user;
}
}
I want to display the save link only when form values have changed. Any ideas how to do this with angular.js?
Give your form a name, such as:
<form name="dataForm">
<input type="text" name="firstname" ng-model="data.firstname" />
<input type="text" name="lastname" ng-model="data.lastname" />
</form>
The form will now be a named model in your scope and you can hide/show the save button based on whether the form is pristine:
<ul ng-hide="dataForm.$pristine">
<li><a><i class="icon-white icon-save"></i></a></li>
</ul>
This approach has the advantage of showing the save button if you change any of the form elements inside the form and the drawback of not checking the input values against their original values, just the fact that they have been edited.
Here is an example of showing your element only when both fields have data:
<div ng-controller="TestCtrl" ng-app>
<ul ng-show="enableSave(data)">
<li><a><i class="icon-white icon-save"></i></a></li>
</ul>
<form>
<input type="text" name="firstname" ng-model="data.firstname" />
<input type="text" name="lastname" ng-model="data.lastname" />
</form>
</div>
And here is your controller:
function TestCtrl($scope) {
$scope.data = {firstname: "", lastname: ""};
$scope.enableSave = function(data) {
return data.firstname.length > 1 && data.lastname.length > 1;
};
}
You can put any logic you want into enableSave. I've chosen to require that they both have at least two characters... you can do whatever you need.
Here is a jsFiddle that illustrates it: http://jsfiddle.net/nDCXY/1/
EDIT by OP: my solutions
$scope.enableSave = function(user) {
if(!angular.equals(user, oldUser)){
return true
}else{
return false;
}
};

Resources