Angular record not binding through directive - angularjs

I am creating a form which you can add multiple columns which maps to an array.
An example of the record model looks like this
record = {'template_name' => 'name', 'detail_section_header' => ['Name', 'Date', 'Assigned To'}
Currently, i'm doing it like this
html:
<form>
<div class="x-label">Name</div>
<input type="text" placeholder="Name" ng-model="record.template_name" autofocus required />
<table>
<tr>
<thead ng-repeat="i in record.detail_section_header track by $index">
<input type="text" detail-section-header-input index="{{$index}}" />
</thead>
</tr>
... <buttons to add/remove columns which increments and decrements the size of detail_section_header> ...
</table>
</form>
directive in coffeescript:
angular.module('xtiri').directive 'detailSectionHeaderInput', ->
link: ($scope, el, attrs) ->
el.on('keyup', ->
$scope.record.detail_section_header[attrs.index] = el.val()
)
What is currently happening is that array (detail_section_header) doesn't populate during the key-up to the bound model (record) until I change another field in the bound model, like the template_name, then it updates. I'm guessing this has something to do with when it digests? Any suggestions on how to make that array populate instantly after typing?

Angular already has ngModel for binding to input elements, so you don't need a custom directive.
Here's how you should use ngModel to handle two-way binding:
<thead ng-repeat="i in record.detail_section_header track by $index">
<input type="text" ng-model="record.detail_section_header[$index]" />
</thead>
Note: Don't try bind to the i variable of the ngRepeat.
For example, this won't do what you expect:
<input type="text" ng-model="i" />
It would be like doing this:
(code just for demo purposes, Angular internals very different)
var detail_section_header = ['Name', 'Date', 'Assigned To'];
var $index = 0;
// This is like using `ng-model="record.detail_section_header[$index]"`
detail_section_header[$index] = 'new value';
// **Correct: **
// We get the expected result,
// `detail_section_header[$index]` has the new value
// This is the equivalent of trying `ng-model="i"`
var i = detail_section_header[$index];
i = newValue;
// **Incorrect:**
// `detail_section_header[$index]` is unchanged

Related

Updating input values filled out by ng-repeat | Angular js

I have to update the value of multiple Inputs using ng-repeat. i usually get the value with JQuery $(class/id).val() and get its value. but now i have no idea how to access the input values since i only have one id for each. (i have like 20 input in the table)
View:
<tr ng-repeat="i in list">
<td><input list="itemNames" class="item_name" ng-model="i.item_name" value="{{i.item_name}}" type="text"/></td>
<datalist id="itemNames">
<option ng-repeat="ii in list" class="idI" ng-model="ii.idI" data-item="{{ii.idI}}" value="{{ii.item_name}} {{ii.idI}}">
</datalist>
<td><input class="quantity" ng-model="i.quantity" value="{{i.quantity}}" type="number"/></td>
<td><input class="price" ng-model="i.price" value="{{i.price}}" type="number"/></td>
<tr>
<td ng-click="updateAll()">UPDATE</td>
</tr>
</tr>
I expect to store all values in an arrays, but what i got is only values of the first row.
JS:
$scope.updateAll=function(){
// getting vallues
var item_name=$(".item_name").val();
var quantity=$(".quantity").val();
var price=$(".price").val();
}
I think your list is a scope variable. So, in controller its defined as $scope.list. You need not to think about id as you are using angular code, angular framework will do it for you.
As you used the two way data binding with ng-model, so any change to the bind variable will be immediately visible to controller and html view.
The use of ng-model ensures the immediate update to the $scope.list variable.
For example, if you add any text for "item_name" in the control index 0 from html view, the variable $scope.list[0].item_name will be automatically updated, also the change in $scope.list[0].item_name = "Some name" in controller will be automatically reflected in the view.
So, your given code can be re-written as following:
<tr ng-repeat="i in list">
<td><input list="itemNames_{{$index}}" class="item_name" id="txt_name_{{$index}}" ng-model="selectedValue" ng-change = "getSelectedId(selectedValue, $index)" type="text"/></td>
<datalist id="itemNames_{{$index}}">
<option ng-repeat="ii in list" class="idI" ng-model="ii.idI" data-item="{{ii.idI}}" value="{{ii.item_name}} {{ii.idI}}">
</datalist>
<td><input class="quantity" ng-model="i.quantity" value="{{i.quantity}}" type="number"/></td>
<td><input class="price" ng-model="i.price" value="{{i.price}}" type="number"/></td>
<tr>
<td ng-click="updateAll()">UPDATE</td>
</tr>
The javascript method can be written as:
var getSelectedId = function(selectedValue, index) {
var val = document.getElementById('txt_name_' + index).value;
var text = $('#itemNames_' + index).find('option[value="' + val + '"]').attr('data-item');
alert(text);
}

how to get select value with input values in angularjs

Can any one help how to get input and selected values by dynamically added with angularjs?
Here is my code in Plunkr
When I select vegtables, another input shows with some values. when I click submit button I need a json like
{
"itemName":"Mango",
"itemType":"fruits"
},
{
"itemName":"Carrot",
"itemType":"vegtables",
"iteminfo":"you selected Carrot"
},
{
"itemName":"Apple",
"itemType":"fruits"
}
I forked your plunker
You can bind itemInfo to an item object using ng-model. Rather than placing the text in a value attribute I just initialized the model value using ng-init.
<tr ng-repeat="item in Items">
<td>
<input type="text" ng-model="item.itemName">
</td>
<td>
<select ng-model="item.itemType">
<option value="">--Select--</option>
<option vaue="fruits">Fruits</option>
<option value="vegtables">Vegtables</option>
</select>
</td>
<td>
<div ng-switch on="item.itemType">
<input type="text" ng-model="item.itemInfo" ng-switch-when="vegtables" ng-init="item.itemInfo='you selected '+ item.itemName">
</div>
</td>
</tr>
Also I had to change the Items array to match your requested naming:
$scope.Items = [{itemName: "Mango"}, {itemName: "Carrot"}, {itemName: "Apple"}];
You can parse the JSON object in a for loop to get the selected value and add the new key value pair.
angular.forEach($scope.vegetables, function(value, key){
if(value.itemName== 'Carrot')
value.iteminfo = 'you selected Carrot';
});
I guess you might be looking for something like this, if I am not wrong.

Angular JS implement Search

I have a form with multiple fields. How do I implement search using angularjs? For example I have the following page:
<pre>
<input name="title">
<input name="isbn">
<input name="author">
<br/>
<input type="submit">
<!-- list the book information -->
Title Author ISBN
</pre>
I want the bottom list to display based on user input on the search fields from the server. Note:The user can input on mutiple fields. How do I accomplish the search when the user clicks the submit button?
Do I need to write one submit method. If so how can I get the values and do the query depending on the field is left blank or not?
Thanks
If your using angularJS you don't need a submit it will do it asynchronously. Just add an ng-model to your input and filter the results based on that. Something like this:
<input name="title" ng-model="title">
<input name="isbn" ng-model="isbn">
<input name="author" ng-model="author>
<div filter: title | filter:isbn | filter:author >
Title Author ISBN
</div>
Something like this
<form name='fred' novalidate='novalidate' ng-submit='submitFunc()'>
<input name='username' required='required' pattern='a-z'>
<input type='email'>
<input type='submit' ng-disabled='fred.$invalid'>
</form>
In your submitFunc you would do an $http request to get your data and filter it and then store the results in an array and do an ng-repeat in the view below the form to iterate over your results
A basic implementation should look like this:
the code:
app.controller('SampleController', ['$scope', function($scope) {
var search = function() {
yourAjaxMethod({author: $scope.author, isbn: $scope.isbn, title: $scope.title}).then(function(list) {
$scope.list = list;
});
}
$scope.$watch('author', search);
$scope.$watch('isbn', search);
$scope.$watch('title', search);
// the init loading
search();
}])
The markup:
<pre>
<input name="title">
<input name="isbn">
<input name="author">
<br/>
<!-- list the book information -->
<table>
<tr>
<th>Title</th>
<th>Author</th>
<th>ISBN</th>
</tr>
<tr ng-repeat="book in list">
<td ng-bind="book.title"></td>
<td ng-bind="book.author"></td>
<td ng-bind="book.isbn"></td>
</tr>
</table>

AngularJS to replace textbox value with expression with a function call

I have an Angular form where ng-repeat is used to create several TDs and each TD element holds a textbox whose value I want to modify using a function call with parameter. I hold values in an array and want to pick a value from there and put in textbox element.
<div ng-controller="MyController" ng-form id="myForm" name="myForm">
<table>
<tr>
<td ng-repeat="myObject in arrayOfObjects">
<input type="text" ng-???="{{ myFunctonCall(myObject.Property) }}" />
</td>
</tr>
</table>
</div>
Is there a standard ng directive or it can be achieved using formatters, parsers?
You can use ng-init to initialize a scope value inside each child scope of ng-repeat. Those values won't be shared with your parent controller, where arrayOfObjects is defined.
https://docs.angularjs.org/api/ng/directive/ngInit
(as said in the doc, do not use too much ngInit)
<div ng-controller="MyController" ng-form id="myForm" name="myForm">
<table>
<tr>
<td ng-repeat="object in arrayOfObjects">
<input type="text"
ng-init="value = myFunctionCall(object.Property);"
ng-model="value" />
</td>
</tr>
</table>
</div>
as #Deblaton Jean-Philippe said you can use ng-model to read the data
<input type="text" ng-model="myinitialvalue" />
then process it in the controller the way you want:
$scope.$watch('myinitialvalue' function(newval, oldval){
//newval is equal to value of myinitialvalue
//here we modify it and assign to myvar, here for example we just add 'abc' to it
$scope.myvar = newval + 'abc';
});
and angular template syntax {{myvar}} to show it in textarea or in some other place if the page:
<textarea>{{myvar}}</textarea>

How to make three-way binding work for child objects in AngularFire?

The following code is getting data from Firebase and binding it to $scope:
'use strict';
angular.module('costumeQueenApp')
.controller('ActorByUserIdActorIdCtrl', function ($scope,$routeParams,FireRef) {
var userId = $routeParams.user_id
var actorId = $routeParams.actor_id
var base = FireRef.actorById(userId, actorId)
$scope.base = base
$scope.base.$bind($scope, 'actor')
})
And uses the following template code to display the data (Note: uses AngularUI-Bootstrap):
<accordion-group heading="Sizes">
<div ng-repeat="(key, value) in actor.sizes">
<label for="key">{{key}}</label> <input type="text" ng-model="value" />
</div>
</accordion-group>
The problem is that three-way data-binding is not working for the items inside the ng-repeat div. If I have an input field that uses the fully-qualified field in an input field, e.g., actor.sizes.waist, everything works as expected.
How do I make the data binding work properly?
If you replace value then you have essentially done: $childScope.value = somethingNew. Like any other JavaScript reference, this dose not modify the contents of the original object.
To put it another way, setting value above is equivalent to this:
var items = [{label: 'a'}, {label: 'b'}, {label: 'c'}];
angular.forEach(items, function(v, k) {
v = {foo: bar}; // does nothing to items! Only modifies the local variable `v`
});
You can fix this when using angularFire by using $set:
<accordion-group heading="Sizes">
<div ng-repeat="(key, value) in sizes">
<label for="key">{{key}}</label> <input type="text" ng-model="value" ng-change="actor.$child('sizes/'+key).$set(value)" />
</div>
</accordion-group>
Note that ng-change feels a bit hackish here but investigating that didn't feel necessary to illustrate the solution.
Also, if you are using $bind to sync data, or you want to control the timing of the sync event, you can just set the data on the parent object without a $set call:
<accordion-group heading="Sizes">
<div ng-repeat="(key, value) in sizes">
<label for="key">{{key}}</label> <input type="text" ng-model="value" ng-change="actor.sizes[key] = value" />
</div>
</accordion-group>

Resources