Angular - Creating an object map - javascript

I need to be able to dynamically create parameter objects in Angular which have the following form:
[
password: {type: 'String', required: true, value: 'apassword'},
someRandomParam: {type: 'Integer', required: false, value:3}
]
I know how to bind to this in order to display the values in a list or table, but I don't know how to construct this object with bindings in Angular, primarily in creating the key ("password") as these keys are created dynamically too.

I'm assuming that you meant having an object, not an array (your code will generate SyntaxError), like that:
$scope.params = {
password: {type: 'String', required: true, value: 'apassword'},
someRandomParam: {type: 'Integer', required: false, value:3}
};
It's true that you can bind to it, even using ngRepeat. But adding something to that object needs something more than just a simple bind. Create some inputs in your view:
<input type="text" ng-model="newParamName" />
<input type="text" ng-model="newParam.type" />
<input type="checkbox" ng-model="newParam.required" />
<input type="text" ng-model="newParam.value" />
<input type="button" ng-click="addParam(newParamName, newParam)" value="Add me!" />
...and in controller:
$scope.addParam = function(name,param){
$scope.params[name] = angular.copy(param);
};

Related

how can I do "twoDataBinding" with formControlName? similar with ng Model

I'm setting a model with form, but I have a problem to set a "MyModel" with a form
This is for the purpose of optimizing the handling of the forms
public myModel = new MyModel();
this.myForm = this.formBuilder.group({
firstName: new FormControl({
value: this.myModel.firstName,
disabled: false
}),
middleName: new FormControl({
value: this.myModel.middleName,
disabled: false
}),
lastName: new FormControl({
value: this.myModel.lastName,
disabled: false
}),
age: new FormControl({
value: this.myModel.age,
disabled: false
})
});
when I submit a "submit" with a button, that shows me the "this.myForm" with the elements that I added in the form
but it seems that I would not be establishing a connection as a "TwoDataBinding"
I also do not want to do this code since I see it very redundant
also
when it comes to many forms and even worse if you decide to change or refactor the attributes of that object
this.myModel.firstName = this.myForm.controls['firstName'].value;
this.myModel.lastName = this.myForm.controls['lastName'].value;
this.myModel.middleName = this.myForm.controls['middleName'].value;
this.myModel.age = this.myForm.controls['age'].value;
You can see the complete code here:https://stackblitz.com/edit/form-model-hammer
of a form model, if you want to make changes makes a FORK to share, thanks:
also for the purpose of avoiding this alert in the picture
If you want to use 2-way binding, you should use template-driven forms instead. It allows you to use ngModel to create two-way data bindings for reading and writing input-control values.
The principles of reactive forms follows the 'one-way' rule, whereby you follow an immutable method of managing the state of your forms, such that there is greater separation of concern between your template and component logic. You can read more about the advantages of reactive forms on the above link.
If you think reactive forms is not what you want, you should revert to using template driven forms, as stated on the first paragraph.
One thing to take note, you should not use ngModel with reactive forms, as this will defeat the purpose of immutability.
However, if you are planning to stick to using reactive forms, you can simplify your code by doing this instead:
1) Initialising and declaring your reactive forms.
this.myForm = this.formBuilder.group({
firstName: [{value: this.myModel.firstName, disabled: true}],
middleName: [this.myModel.middleName],
lastName: [this.myModel.Name],
age: [this.myModel.age],
});
2) Get reactive form data:
// since the properties of myModel and myForm FormControl names are the same
this.myModel = {...this.myForm.value};
3) Update reactive form data:
this.myForm.patchValue(this.myModel);
You should stop using ngModel with Reactive forms as it is deprecated now .
You can simplify the code like below :
this.myModel.firstName= 'FIRST NAME';
this.myForm = this.formBuilder.group({
firstName:[{
value: this.myModel ? this.myModel.firstName : '',
disabled: false
}],
middleName: [{
value: this.myModel ? this.myModel.middleName : '',
disabled: false
}],
lastName: [{
value: this.myModel ? this.myModel.lastName : '',
disabled: false
}],
age:[{
value: this.myModel ? this.myModel.age : '',
disabled: false
}]
});
Listen to (ngOnSubmit) event and write a function to save the form values .
This can be achieved in the below way :
save(myForm : any){
let form = myForm as MyModel;
console.log(form);
form.otherProperties = 'nothing';
}
Or :
save(myForm : MyModel){
let form = JSON.parse(JSON.stringify(myForm)); //for copy
console.log(form);
form.otherProperties = 'nothing';
console.log( 'added a new property', form);
}
And in your html :
<div>
<form [formGroup]="myForm" (ngSubmit)='save(myForm.value)'>
<label>
<span>
Fisrt name:
</span>
<input type="text" formControlName="firstName">
</label>
<label>
<span>
Middle name
</span>
<input type="text" formControlName="middleName">
</label>
<label>
<span>
Last name
</span>
<input type="text" formControlName="lastName">
</label>
<label>
<span> Age: </span>
<input type="number" formControlName="age">
</label>
<div style="display: block">
<button (click)="onShowModel()">
show model
</button>
</div>
<div style="display: block">
<button>
set model from form
</button>
</div>
</form>
</div>
<div>
<p class="red"> from model : </p>
<span class="red"> Model: {{myModel | json}} {{nothing}}</span>
</div>
<div>
<p class="blue"> from form, binding model : </p>
<span class="blue"> Model: {{myForm.value | json}}</span>
</div>
I have also forked your example : Example

"Cast to Number failed for value 1,1 at path"

I'm trying to build a user model with some privileges.
Schema looks like this:
var mongoose = require("mongoose"),
passportLocalMongoose = require("passport-local-mongoose");
let userSchema = new mongoose.Schema({
username:
{type: String,
unique: true
},
password: String,
privileges:
[{
region: { type: Number, unique: true },
read: Number,
write: Number,
edit: Number
}]
});
userSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", userSchema);
POST route looks like this.
router.post('/register', function(req, res)
{
console.log(req.body);
User.register(new User({
username: req.body.username,
privileges:{
region: req.body.privileges['region'],
read: req.body.privileges['read'],
write: req.body.privileges['write'],
edit: req.body.privileges['edit']
}
}), req.body.password, function(err)
{
if(err)
{
console.log(err);
res.redirect("/register");
}
else
{
console.log("fine");
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login'
})
}
})
});
<form action="/register" method="POST">
<input type="text" name="username"><br/>
<input type="password" name="password"><br/>
<input type="text" name="privileges[region]"><br/>
<input type="text" name="privileges[read]"><br/>
<input type="text" name="privileges[write]"><br/>
<input type="text" name="privileges[edit]"><br/>
<input type="text" name="privileges[delete]"><br/>
<button>Submit</button>
</form>
Basically, it should work like this:
From the form I should get an array of privileges.
Now when I enter data in the fields like this:
test
1234
1 2
1 1
1 1
1 1
(test - username, 1234 - password, 1 2 region array, 1 1 - read array, 1 1 write array, 1 1 edit array) I get this error:
Now I get the reason - privileges[edit] is type="text" and it can't be parsed into the DB as a Number. But why does it happen ONLY for edit? I find it strange.
I tried changing input type to number, but after that I can't enter an array anymore.
I think I might need a middleware which transforms the text into numbers. Am I right? If so, how should it do it? Should it transform each element of the array individually or the array as a whole?
Thanks.
In the schema defined for User, privileges is an array of SubDocuments having this schema.
{
region: { type: Number, unique: true },
read: Number,
write: Number,
edit: Number
}
When setting this field, the data provided needs to match that schema. e.g.
new User({
username: req.body.username,
privileges: [
{
region: ":region_value",
read: ":read_value",
write: ":write_value",
edit: ":edit_value"
},
{
region: ":region_value",
read: ":read_value",
write: ":write_value",
edit: ":edit_value"
},
//....
],
})
I assume that the design for privileges was done purposely in this way to allow for a user to have many privileges.
A straightforward way to set privileges is to design the form appropriately. The form field can allow for setting several privileges. For example, to set two privileges, you can achieve that by writing the markup this way:
<input type="text" name="privileges[0][region]"><br/>
<input type="text" name="privileges[0][read]"><br/>
<input type="text" name="privileges[0][write]"><br/>
<input type="text" name="privileges[0][edit]"><br/>
<input type="text" name="privileges[0][delete]"><br/>
<input type="text" name="privileges[1][region]"><br/>
<input type="text" name="privileges[1][read]"><br/>
<input type="text" name="privileges[1][write]"><br/>
<input type="text" name="privileges[1][edit]"><br/>
<input type="text" name="privileges[1][delete]"><br/>
This way privileges in the req.body will have the right format e.g.
{ privileges:
[ { region: '1', read: '2', write: '2', edit: '2', delete: '4' },
{ region: '2', read: '4', write: '4', edit: '4', delete: '4' } ] }
So that you can simply write
new User({
username: req.body.username,
privileges: req.body.privileges
})
It's more straightforward to ensure the client passes the right data than trying to massage the data after the fact.
A limit in the design of the form this way means that the number of privileges that a user can have has to be determined ahead of time. A work around this is to build the form dynamically and give control to the user to add more privileges as the case may be. See the following example to get an idea about how to go about it:
function addPrivilege(e) {
e.preventDefault();
const privileges = $('.privileges');
const lastCount = privileges.length;
console.log($(this).data('template').replace(/:x:/g, lastCount))
const template = $(this).data('template').replace(':x:', lastCount);
privileges.after($('<div class="privileges"></div>').append(template))
}
$(document).ready(function () {
$("#addPrivilegeBtn").on('click', addPrivilege);
});
.privileges {
background: #ccc;
padding: 8px 16px;
margin: 4px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<div class="privileges">
<input type="text" name="privileges[0][region]"><br/>
<input type="text" name="privileges[0][read]"><br/>
<input type="text" name="privileges[0][write]"><br/>
<input type="text" name="privileges[0][edit]"><br/>
<input type="text" name="privileges[0][delete]"><br/>
</div>
<button
id="addPrivilegeBtn"
data-template='<input type="text"name="privileges[:x:][region]"><br/>
<input type="text" name="privileges[:x:][read]"><br/>
<input type="text" name="privileges[:x:][write]"><br/>
<input type="text" name="privileges[:x:][edit]"><br/>
<input type="text" name="privileges[:x:][delete]"><br/>'
>Add privilege</button>
</form>

How to add values into JSON-array (node.js/mongoose)

I'm using mongoose, adding values from HTML and saving to db with help of mongoose. I'm having issue adding value from req.body.chapter into array from HTML.
Route:
const newBook = {
book: req.body.book,
summary: req.body.summary,
chapters: //how to add value into chapter-array?
}
Book-model:
const BookSchema = new Schema({
Title: {
type: String,
required: true
},
Summary: {
type: String,
required: true
},
chapters : [{
chapter: {
type: String,
required: true
}]
});
HTML:
<div class="form-group">
<label for="book">Title:</label>
<input type="text" class="form-control" name="book" required>
</div>
<div class="form-group">
<label for="Summary">Summary:</label>
<input type="text" class="form-control" name="Summary" required>
</div>
<div class="form-group">
<label for="chapter">chapter:</label>
<input type="text" class="form-control" name="chapter" required>
</div>
So if the data looks like this, you can just put it in:
req.body.chapters = ["1", "3"];
Route:
const newBook = {
book: req.body.book,
summary: req.body.summary,
chapters: req.body.chapters
}
If you just want to add the chapters to existing data, then have a look at Using Mongoose / MongoDB $addToSet functionality on array of objects

how to add elements into a form by using ng-click?

I have a form that has 4 fields
<form name="form">
Name:
<input type="text" ng-model="edit.name"><br />
age:
<input type="text" ng-model="edit.age"><br />
phone:
<input type="text" ng-model="edit.phone"><br />
address:
<input type="text" ng-model="edit.address"><br />
</form>
And i have an array in the app.js
$scope.statuses = [
{value: 1, text: 'status1'},
{value: 2, text: 'status2'},
{value: 3, text: 'status3'},
{value: 4, text: 'status4'}
];
I am repeating that array in the html and what i want to do is that i want the text to appear in the form when a button such as "edit" is clicked
PLUNKER
So basically as you can see in the plunker when i click on the edit button i want "status1" "status2" "status3" "status4" to appear in the fields that are in the form "name" "age" "phone" and "address"
Your question was a bit unclear, but I think what you want to do is change statuses to the form values.
I create this plunker for you
on your ng-click function. I change the statuses array text (which is shown with ng-repeat in your html) to your forms edit values:
$scope.editFunction = function(edit){
$scope.statuses[0].text=edit.name;
$scope.statuses[1].text=edit.age;
$scope.statuses[2].text=edit.phone;
$scope.statuses[3].text=edit.address;
};
or if you want your array values be in your form just reverse the = in your function like plunker :
but in this case,if you want your empty form to work, you should declare edit before in your controller.
$scope.edit=[];
$scope.editFunction = function(edit){
edit.name=$scope.statuses[0].text;
edit.age=$scope.statuses[1].text;
edit.phone=$scope.statuses[2].text;
edit.address=$scope.statuses[3].text;
};
and so you are using ng-model.there is no need to pass argument to your function, you can use $scope.edit instead of a passing argument in your editFunction

Autoform insert: doc value isn't having any effect

I have this quickForm:
{{> quickForm id="insertFixie" collection="Fixies" type="insert" doc=seedObject}}
Backed up by this schema:
Fixies = new Meteor.Collection('fixies');
Schema.Fixies = new SimpleSchema({
description: {
type: String,
label: "Description",
trim: true,
optional: true
},
cost: {
type: Number,
label: "Cost",
min: 0,
decimal: true
},
product_id: {
type: String,
autoform: {
omit: true
}
},
});
Fixies.attachSchema(Schema.Fixies);
and this seedObject method:
Template.insertFixie.helpers({
seedObject: function () {
console.log({product_id: this._id});
return {product_id: this._id};
}
});
When that console call directly above happens, it's correct and gives something to this effect:
Object {product_id: "1"}
But when I submit the form with something valid (like "stuff" and "100"), I get this error:
insert error:
Error: Product is required {invalidKeys: Array[1],
validationContext: SimpleSchemaValidationContext,
stack: (...),
message: "Product is required"}
stating that the product_id attribute is required and currently has a value of null.
What am I doing wrong? That product_id is a template dependent value, so something like "autoValue" in the schema doesn't seem like the best way to handle it.
The docs seem to clearly state that I'm using things correctly. From the description of the doc attribute of Auto Form:
For an insert form, you can also use this attribute to pass an object
that has default form values set (the same effect as setting a value
attribute on each field within the form).
And from the description of the value attribute of afFieldInput:
value: Set a specific, potentially reactive, value for the input. If
you have also provided a doc attribute on the autoForm or quickForm,
this value will override the value from the doc object.
What am I missing?
Edit
I added an autoValue field to my schema, just to see what pops up:
autoValue: function (doc) {
console.log(doc)
console.log(this.value)
return "1";
}
This allows the form to correctly submit, but with the incorrect hard-coded value of "1" rather than a useful value from the template. The two console logs show this:
:24 Object {description: "stuff", cost: 50}
:25 undefined
It seems my seedObject value isn't available to autoValue.
Do I have to hijack the onSubmit hooks? Do I have to have hidden form inputs with values supplied from the template? What's the fix here?
It turned out to be a hidden input.
I expanded my form to this:
{{#autoForm id="insertFixie" collection="Fixies" type="insert"}}
<fieldset>
{{> afFormGroup name="description" placeholder="schemaLabel" label=false}}
<div class="form-group{{#if afFieldIsInvalid name='cost'}} has-error{{/if}}">
<div class="input-group">
<div class="input-group-addon">$</div>
{{> afFieldInput name="cost" placeholder="schemaLabel" label=false}}
</div>
{{#if afFieldIsInvalid name="cost"}}
<span class="help-block">{{afFieldMessage name="cost"}}</span>
{{/if}}
</div>
{{> afFormGroup name="product_id" type="hidden" value=_id}}
</fieldset>
<button class="btn btn-primary" type="submit">Insert</button>
{{/autoForm}}
Adding an afFormGroup with type="hidden" did exactly the trick.
Although it still seems to me like the doc argument isn't living up to it's promises.

Categories