When binding input value to an ng-model like so:
<input type="text" ng-model="array">
how do I bind the input text as an array? So if I input one, two, three, the resulting model will be [ "one","two","three ].
Right now this is how I'm achieving this:
<input type="text" ng-model="string" ng-change="convertToArray()">
And in my controller:
$scope.convertToArray = function(){
$scope.array = $scope.string.split(',');
}
It works fine but I don't think it's best practice because I'm making a $scope.string variable and then hardcoding the destination array.
Is it possible to just have the input's model set into array and then have the input pass through the function before being bound to the scope?
ngList will do exactly what you want.
Text input that converts between comma-separated string into an array of strings.
No. Since your input will be a string from the user, you need to manually convert it to array or any other format.
Similar discussion and other approach is discussed here. probably this might help. (see accepted answer in this)
How do I set the value property in AngularJS' ng-options?
Related
I have some inputs, and I want to get data in order of insertion, for example: if I insert the value bbb then the value aa I want to get bbb befor aa
I search in the net and find that this order is ensured using Mapbut I don't know how to use it with ng-model.
thank you in advance.
EDIT
I'm using an object that store the value of the inputs and a customized key passed with value
here is an example, if you insert the values in input 3 then 2 then 1, and click ok, in the console the output will be ordered in an alphabetic order
As stated by #czosel, javascript objects are not ordered, and are usually sorted by alphabetical order of the keys. Therefore, your best solution is probably going to involve going beyond using the ng-model directive as is.
Here are two possibilities you could try out:
Solution 1
In every <input /> place an ng-blur directive that will determine the input's order. For instance:
HTML
<input ng-blur="onBlur('model1')" ng-model="model1" />
<input ng-blur="onBlur('model2')" ng-model="model2" />
controller.js
app.module('myModule').controller('myCtrl', ['$scope', function($scope) {
$scope.count = 0;
$scope.onBlur = function(key){
// check if anything was entered
if($scope[key]){
// make sure this is first time data was entered into this input
if(!$scope[key].order)
$scope[key].order = $scope.count++;
}
};
}]);
Solution 2
Store the values in an array. Similar to the first solution, but instead of keeping count, you would forego the ng-model altogether and manually add the value to an array (after checking that it doesn't already exist, which gets a little tricky with an array). Of course you also have to handle updates yourself, so the first method is definitely going to be simpler. The lodash library will probably be of much help if for some reason you decide to choose this approach.
Lots of luck!
JavaScript Object properties have no guaranteed order, see this answer.
Try using an array instead.
You can Queue(First in First Out) to get data in the order of insertion. Trigger a function and store the values binded in ng-model into queue.
Ex: ng-model = data // here data will be bbb
var queue = [];
function bind(value){
queue.push(value); // value will be bbb
}
if user enters aa then again bind function needs to be called to push the value inside queue
U can get the values in the order of insertion.
I'll try to make this as simple as possible.
Problem:
I've got a page where I need to have X amount of <input> tags generated depending on the value chosen in a <select>.
Stack/Tech: Angular2/Javascript
Example:
The dropdown asks, "How many kids do you have?"
If the user selects 3 in the <select> dropdown, then 3 total <input> fields are generated and inserted in the <div> right after the question.
What I've Tried:
I've successfully pulled it off already but it's semi-unreliable and I don't feel like this is the "proper" way to do things.
Failure 1:
I tried using *ngFor with conventional for loop logic. I just did something like this:
for(let i = 0; i < selectValue; i++)
It didn't like that. Apparently *ngFor is mostly used to iterate through arrays and objects and can't be used with conventional for logic.
Failure 2:
I tried to pre-generate an array of objects, a multidimensional array, and a JSON-style object with empty but pre-filled fields.
My goal was to generate X objects in an array where X corresponds to the select number that the user chose. Then the *ngFor could iterate through this "pre-filled" array, display the inputs, and then I would fill that object later to be sent over HTTP.
Basically when the <select> changes it would call a function while passing the number the user chose. That function would generate a new array with X amount of objects.
No idea why this didn't work.
Success (but isn't ideal):
I tried binding [innerHTML] = generatedHTML and then filled the generatedHTML variable with traditional Javascript. This made <input> that seemed to worked fine, but this seems like a HORRIBLE way to do this. Plus I need to create event binders on each of these new <input> and I'm not sure if manually inserted [innerHTML] will work properly within Angular2's zones if I insert it in such a terrible fashion.
Bonus Question
After getting the inputs added I'd like to access their values in the easiest and most complete way possible. Is it possible to add multiple data inputs to ONE ngModel and then access each individual input through iteration? Or do I need to figure out another way to do this? Could I just put all those inputs under one <form> and then tie that <form> to a single data model?
Thanks for the help everyone! Pretty new to Angular 2 but digging it a lot so far :)
I would do it like this:
Assign a ng-change function to your select
Process the ng-model bound value of the select in that function (to create a new Array with n-Elements)
Add whatever info to each inputArray element you need later to decorate the input boxes
Do a ng-repeat over that array to create your input's
HTML:
<select [(ngModel)]="number" (ngModelChange)="nrChanged()">
<option *ngFor="let nr of numbers" [value]="nr">{{nr}}</option>
</select>
<input *ngFor="let inputInfo of inputArr" type="text" placeholder="text" />
Component:
private numbers = [1,2,3,4,5,6,7];
private number;
private inputArr = [];
nrChanged()
{
this.inputArr = [];
for(var i = 0; i < this.number; i++){
/*here you can assign any information you need for creating the concrete input */
this.inputArr[i] = {neededInfo: 'I am the ' + i + 'Element'};
}
I have an array in $scope, say
$scope.my_array = ["val_1", "val_2", "val_3"]
To bind this array input element, I used ng-model:
<input type="text" ng-model="my_array">
Now I want it to be display the array values as comma separated in input box, but nothing displays. Is this even possible?
In ng-repeat, it is iterating the values, so the array is available to the view.
EDITED: Thanks, the normal way is working for array binding. But in my case I was first using empty array:
$scope.my_array = []
Then, on ng-click function, I am grabbing the data-* attribute from the clicked element and pushing it to the array:
var item = $(".some-class").data("field-type");
$scope.my_array.push(item)
Iterating over this is working fine, but not working while setting to the input.
Look at another topic where two-way filtering is explained in details: How to do two-way filtering in angular.js?
in brief you should use ngModels' $parsers and $formatters collection to be able make .join(", ") before setting to input and to make .split(/, */) before setting value back to the model.
This problem has been solved, I used
$scope.my_array = $scope.my_array.concat(item)
Instead of using .push() method.
I don't know if the push method of the array is a problem, but after concating the value into array, worked for me, now the array values are visible in input field.
I am trying to do the following quite unsuccessfully so far.
I have an string that is semicolon separated. Say a list of emails, so
'email1#example.com;email2#example.com;email3#example.com'
What I am trying to accomplish is split this string (using split(';')) into an array of strings or array of objects (to aid binding). Each of the items I would like to bind to different input elements. After editing I want to read the concatenated value again to send to my backend.
Problem is that when editing one of the split inputs, the original item value is not update (which makes sense as I am guessing the individual items are copies of parts of the original), but I am wondering if there is a way to do something like that.
Note that I want this to go both ways, so watching the individual inputs and updating the original one manually, would just fire an infinite loop of updates.
I have tried a few different ways, including creating an items property get/set using Object.defineProperty to read and right to the string (set was never fired).
take a look at this plnker
You can construct a temporary array on each field update in order to do the string replacement of the old segment with the new value. In order to tackle the lost focus problem you will have to use the ngReapeat's track by $index. The internal array will not be recreated unless you add the separator to your original string.
Here is the complete solution on Plunker
Your main issue is your ng-model attribute on your repeated input element. I would start with making use of ng-repeat's $index variable to properly bind in ng-model. In your original Plunker 'name' is NOT a scope property you can bind to, so this should be changed to ng-model="names[$index]"
Here is a Plunker to reflect this. I made quite a few changes for clarity and to have a working example.
NOTE: You will find that when editing fields directly bound to a repeater, every change will fire a $digest and your repeated <input> elements will refresh. So the next issue to solve is regaining focus to the element you are editing after this happens. There are many solutions to this, however, this should be answered in a different question.
Although binding to a string primitive is discouraged, you could try ng-list.
<form name="graddiv" ng-controller="Ctrl">
List: <input name="namesInput" ng-list ng-model="vm.names"/>
<ul>
<input ng-repeat="name in vm.names track by $index" ng-model="name" ng-change="updateMe($index, name)"/>
</ul>
You'll need both track by $index and an ng-change handler because of the primitive string binding.
function Ctrl($scope) {
$scope.vm = {}; // objref so we can retain names ref binding
$scope.vm.names = ['Christian', 'Jason Miller', 'Judy Dobry', 'Bijal Shah', 'Duyun Chen', 'Marvin Plettner', 'Sio Cheang', 'Patrick McMahon', 'Chuen Wing Chan'];
$scope.updateMe = function($index, value){
// ng quirk - unfortunately we need to create a new array instance to get the formatters to run
// see http://stackoverflow.com/questions/15590140/ng-list-input-not-updating-when-adding-items-to-array
$scope.vm.names[$index] = value; // unfortunately, this will regenerate the input
$scope.vm.names = angular.copy($scope.vm.names); // create a new array instance to run the ng-list formatters
};
}
Here's your updated plunkr
I want to render an array of possible authors as inputs so that which authors are assigned can be edited.
.form-multiple-item(ng-repeat="author in story.authors")
input(type='text', ng-model="author")
Problem is if I want to add an additional blank input after the existing inputs. How can I do that?
.form-multiple-item(ng-repeat="author in story.authors")
input(type='text', ng-model="author")
input(type='text', ng-model="author")
For example this wouldn't work as author wouldn't be on the correct scope.
If I have story.authors = [""] I want to render an blank input for the user to fill out. But that wouldn't work either as "" just gets ignored by ng-repeat instead of rendering an empty input to be filled. How do I do I either render an empty input or perhaps get another ng-model somewhere inserted into an array in another scope.
I think the Angular way of doing this to "put a dot in your model". Your authors model should be an object instead of a string: { name: '' } With the aforementioned object, you should be able to represent an empty input in your ng-repeat.
The ng-model in the <input> would look like this:
<input type="text" ng-model="author.name" />
#Harry You asked why complicate things?
The reason is so that assignments work. The input controller will assign its value to whatever's in "ng-modal". If it's a normal variable, then the link to the original list will be lost. If it's an attribute of an object, then the link is preserved. Consider the two scenarios:
for author in authors:
author = "joe"
See how authors doesn't get changed?
for author in authors:
author.name = "joe"
Now the connection is preserved.
Does that make sense?