AngularJS inconsistent databinding - angularjs

I'm learning AngularJS and I have a question regarding the databinding for select elements. The databinding for textboxes works without any kind of event handling code. Once the ng-model attribute is set textbox updates when the model property changes and vice versa. There is no need for ng-change attribute.
However, for select elements we need to write functions that will be called via ng-change atribute.
Why does angularjs handle databinding without an ng-change attribute for textboxes but requires functions that will be called via ng-change attribute for select elements?
UPDATE:
Added the fiddle in the comments section. The example is from AngularJS in Action book. Click on one of the stories, change the textbox value and the model is updated. Change the selection in dropdown model is not updated.
UPDATE:
Added a new fiddle in the comments.
Thanks.

I've created a fiddle that works here - The issue is really just the dummy data here. In the original fiddle, the object created in the statuses array for {name:'Back Log'} and {name:'To Do'} are not the same (not ===) as the {name:'Back Log'} and {name:'To Do'} objects created in the dummy story objects.
To make the example work, I pass the indexed statuses into the getStories function. However I think this is really just a case of demo-induced confusion. (I've been looking at the MEAP for Angular in Action as well, and I think it could be simplified a bit like this one, that uses simple string statuses that will pass the === test
var getStories = function(statusesIndex) {
var tempArray = [
{title:'Story 00',
description:'Description pending.',
status: statusesIndex['To Do']
},
{title:'Story 01',
description:'Description pending.',
status: statusesIndex['Back Log']
}
];
return tempArray;
}

I think your confusion might be a result of the select documentation still being incorrect. (See my Disqus comment.) ng-model can and should be used with select. ng-change is optional and it just gives you a hook should you want to do something each time the selected option changes.
Normally you should use ng-options with select.

If i understood your question correctly then I think your guessing is wrong because for select boxes, you do not have to invoke ng-change event in order to fetch the selected option.
<select ng-model='select'>
<option>....</option>
<option value='one'>One</option>
<option value='Two'>Two</option>
</select>
// Your selected option will print below... without invoking ng-change
<div>You selected: {{select}}</div>
Demo: http://jsfiddle.net/jenxu/1/

Related

Using contenteditable with ng-model inside ng-repeat?

Here is my issue:
I am using ng-repeat to make a list of spans.
Each span has the contenteditable attribute and ng-model directive.
Everything works as expected (including two-way data binding), until I try to add a new item to the list.
<div ng-repeat="item in list">
<span ng-model="item.text" contenteditable></span>
</div>
<button ng-click="addItemToList"></button>
The methods look like this:
$scope.addItemToList = function () {
$scope.list.push({text: 'dummy text'});
}
or
$scope.addItemToList = function () {
$scope.list.splice(1, 0, {text: 'dummy text'});
}
When adding the new item to the list (push or splice), the DOM updates, but the last item is initialised empty, with no text whatsoever. The last item in the model list also empties out, even if I specifically push an element with text in it.
After a few tests, I've noticed that this only happens if the list's length is bigger after modifying it:
if I try to replace/modify/remove (not add) an element in the list, it works well.
I believe this has to do with the way contenteditable elements initialise in the DOM (I think they initialise empty, and somehow, the model empties out as well).
Has anyone encountered this problem before? If yes, how did you solve it / what workaround have you found?
Based on the angular docs related to ngModelController, it seems that there is not built-in support for two-way data binding with contenteditable elements, seeing as in the plunkr example they wrote their own contenteditable directive. You might be able to use a modified version of that as a workaround.
It looks to be a similar problem as this question and the contenteditable directive there looks similar to the contenteditable directive in the angular docs example.
I also found this directive on github that might accomplish what you are trying to do.
Edit: I did a new version of the plunk I posted in the comment above:
https://plnkr.co/edit/v3elswolP9AgWHDIPwCk
In this version I added a contenteditable directive that appears to be working correctly. It is basically a spin off of how the input[type=text] directive is written in angular, but I took out the places where it handles different types of input (since in this case it will just be text) and the places where it handles events that contenteditable elements don't even fire. I also changed it so that the $viewValue gets updated based on element.html() instead of element.val(). You might be able to use something like this as a workaround
The issue is old but that was the same problem for me today. (angular 1.5). My workaround was to add on blur update option: <td contenteditable data-ng-model="position.value" ng-model-options="{updateOn: 'blur'}"></td> somehow then model stopped getting be empty on initialize. I like using update on blur in many places (solves some performaces issues) so this is good for me, however it's some kind of trick.

md-select attribute name error Angular-Material

Im working with angularjs/1.3.6 and v0.10.0/angular-material.js with a md-select field with name="txtGenero" but when the form have been Submitted with ajax the md-selected has a dot at beginning of name field like '.txtGenero' in the image below.
https://plus.google.com/photos/+TommyHern%C3%A1ndezA/albums/6175087393788917697/6175087392180342338?pid=6175087392180342338&oid=118016940134163401130
I hope can help me thanks.
I don't exactly know why the ngFormController has the .txtGenero field.
However you should use ngModel to bind your form fields to a model and post that model, not the form controller itself. You can use angular.element.param($scope.registro) to get the the fields from your model as form data.
You don't need jQuery for any of it.
Updated plunkr: http://plnkr.co/edit/IFOsFgiQBo7vJVzkBxaf?p=preview
(I updated to angular-material master)
Try to provide your code well formatted, this is kind of a mess to work with.

Don't show dropdown choices with angular-ui-select before entering data?

I am using the angular-ui-select directive to create an auto-complete input field. I want to be able to click and focus the field without the dropdown options appearing. The Plunker example in the documentation works this way, but I cannot get mine to behave correctly. Please help.
Here is my code:
<ui-select ng-model="customer.selected" theme="bootstrap">
<ui-select-match placeholder="Start typing..">{{ $select.selected.family_name }}</ui-select-match>
<ui-select-choices repeat="customer in customers | filter: $select.search">
<div ng-bind-html="trustAsHtml((customer.family_name | highlight: $select.search))"></div>
</ui-select-choices>
</ui-select>
Here is their Plunker example that is working the way I would like it to.
http://plnkr.co/edit/a3KlK8dKH3wwiiksDSn2?p=preview
I have a same issue. I want to be able to click and focus the field without the dropdown options appearing.
Here I am sharing my solution. May be it will help someone. I made changes in Selectize theme example in plunker shared by #Corey Quillen. I had cleaned up other things so it will help.
1) Here first I had add reset-search-input="false" in ui-select directive. So it will not reset search input.
2) Then I had replaced filter with custom filter propsFilter. Which is already useing in Select2 theme. And put
if(!props.name.length){
return out;
}
in filter above If condition. In demo.js file.
So data return empty if no search text entered.
Here is the plunker example of that.
ui-select by default shows a dropdown, I dont think we can change that. But you can change its binding data. In the plunker example the ui-select is bind to model 'address.selected', which initially is empty, thus nothing pops up. But once you start typing the refresh will call the function refreshAddress() to populate the results into address. Once the results are populated, ui-select finds that its model(address) has data and start showing a dropdown. After you have searched for something, try to click on the textbox, it will still show you the result, beacuse they are still present in the model.
Since, you are trying it with a local variable I suppose, it is pre-populated with data for ui-select and hence it shows it. I think you should try to make a request in you code to get that data and use refresh and refresh-delay. If you dont have a web service and want to use local data, I would suggest bind the select to an empty model and put data into that using a custom function for refresh, you might have to write a custom search functionality into the refresh function, but you can use javascript's search() or indexOf() for that.

AngularJS: how put correct model to repeated radio buttons

I think I have some sort of special code here as all I could google was "too simple" for my problem and it also didn't helped to come to a solution by myself, sadly.
I got a radio button group of 2 radios. I am iterating over "type" data from the backend to create the radio buttons.
My problem is the data binding: When I want to edit an object its "type" is set correctly, but not registered by the view so it doesn't select the desired option.
Follwing my situation:
Backend providing me this as "typeList":
[
{"text":"cool option","enumm":"COOL"},
{"text":"option maximus","enumm":"MAX"}
]
HTML Code:
<span ng-repeat="type in typeList track by type.enumm">
<input
type="radio"
name="type" required
ng-model="myCtrl.object.type"
ng-value="type">
{{type.text}}
</span>
Some Explanation
I don't want to use "naked" texts, I want to use some sort of identifier - in this case it is an enum. The chosen value shall be the entire "type", not only "type.text" as the backend expects type, and not a simple String.
So all I do with this is always a package thingy, the type.text is for like formatted/internationlized text etc.
A Pre-Selection works by setting this in the controller: this.object.type = typeList[0];
The first radio button is already selected, wonderful.
But why isn't it selected when editing the object. I made a "log" within the HTML with {{myCtrl.object.type}} and the result is {"text":"cool option","enumm":"COOL"}. The very same like when pre selecting. I already work with the same "technique" using select inputs, and it works fine. I also found some google results saying "use $parent because of parent/child scope". But 1) I didn't get that straight and 2) think it is not the problem here, as I use a controllers scope and not the $scope, or is this thinking wrong?
It might be explained badly, sorry if so, but I hope someone 1) get's what I want and 2) knows a solution for it.
Thank you!
If you're trying to bind to elements from an array, I believe you need to assign the actual elements from the array to your model property.
So this creates a new obj and sets it to $scope.selectedType (not what you want):
$scope.selectedType = {"text":"cool option","enumm":"COOL"};
whereas this assigns the first element of the array (which is what you want)
$scope.selectedType = $scope.typeList[0];
So to change the model, you can lookup the entry from the array and assign it to your model with something like this
$scope.selectedType = $scope.typeList.filter(...)
Here's a quick example of this approach http://plnkr.co/edit/wvq8yH7WIj7rH2SBI8qF

AngularJS drop down (ng- options) not binding - string to object (initial selection)

I am having a problem binding data retrieved from the server to a drop down list. The main issue I think is the fact that the comparison is done on differing object types.
For example:
1. The object returned from the server contains a currency code string. we want this to be bound to an item in the dropdown list
"baseCurrencyCode":"GBP"
The view model returns the list of currencies.. These are returned as a list of currency objects with different properties
{"currencies":[{"id":1,"rateId":0,"abbreviation":"AFN","description":"Afghani","rate":0.0,"rateDescription":null,"languageCode":"en-gb","isDefault":true,"fullDescription":"AFN - Afghani - ","shortDescription":"AFN - Afghani"}}
etc.
Currently, I have got this working by writing a function to go through every property for every item in the list, find the correct property we wish to compare to - do the comparison and then return the initial selection.
When calling my save method I then need to manually bind the currency abbreviation to the object I wish to return to the server.
Surely there must be a better way to do this?
Some of my code for reference..
<select ng-model="selectedCurrency" ng-options="currency.shortDescription for currency in viewModel.currencies"></select>
// Call to my custom method..List, PropertyName, value to compare
$scope.selectedCurrency = InitialiseDropdown($scope.viewModel.currencies, "abbreviation", $scope.updatedObject.baseCurrencyCode);
// Code executed when saving - to bind the currency to the updated object
$scope.updatedObject.baseCurrencyCode = $scope.selectedCurrency.abbreviation;
Any help is appreciated!
EDIT
Sorry if I wasn't clear enough.. To summarise..
The main problem here is binding to the drop down and initial selection.
The object we are updating contains a parameter (string) of the currency abbreviation.
The list we select from is a list of currency objects. As these are two differing object types I have been unable to plug in angulars 2 way binding and have written some code to do this on initial retrieval and when saving.
The cleanest way to fix this would be to return a currency object in our retrieval instead of a simple string of the abbreviation, but this is not an option.
Is there a better way of enabling 2 way binding on different object types ?
Thanks again
It is not exactly clear what the problem is, but you can save yourself some work by binding the <select> to the currently selected currency object (so you don't have to look it up later).
select + ngOptions allow you to bind to one value while displaying another, with the following syntax:
<select ng-model="selectedCurrency"
ng-options="currency as currency.shortDescription
for currency in viewModel.currencies">
</select>
In the above example, $scope.selectedCurrency will be bound to the whole currency object, but currency.shortDescription will be displayed in the dropdown.
See, also, this short demo.
UPDATE:
In case you don't need to bind to the whole currency object, but just bind updatedObject's baseCurrencyCode property to the abbreviation of the selected (in dropdown) currency, you can do it like this:
<!-- In the VIEW -->
<select ng-model="updatedObject.baseCurrencyCode"
ng-options="c.abbreviation as c.shortDescription
for c in currencies">
</select>
// In the CONTROLLER
$scope.currencies = [...];
$scope.updatedObject = {
...
baseCurrencyCode: <baseCurrencyCodeFromServer>
};
See, also, that short demo.
I have had the same problem, ng-model and ng-option being from 2 different sources. My ng-model is bound to a value in a json object representing a chosen filename and my ng-option is a list of possible values taken from a csv file.
In the controller I am reading a directory via a Nodejs route, and creating a json array of filenames like this
var allCsvFiles = [{"name":"file1.csv"},{"name","file2.csv},etc..]
The current csv file, i.e. the selected one is stored in another json array
[{"date":"01-06-2017","csvfile":"file1.csv","colour":"red"},{...}, etc].
I was using the following code for the dropdown:
<select type="text" ng-model="file.csvfile"
ng-options="opt.name for opt in allCsvFiles track by opt.name"></select>
Which caused the current selection to be blank and if I selected an item from the dropdown it put [object],[object] as the current selection. If I stepped through the code I found that it seemed to be selecting {"name","file1.csv"} as the option and couldn't display it, this seemed odd as my ng-options selection looks like it should just return the value of "name" not the array entry. I tried many different ways to make this work but eventually I found that if I made the list of possible selections a plain javascript array:
var allCsvFiles = ["file1.csv","file2.csv", "file3,csv]
and changed the select to:
<select type="text" ng-model="file.csvfile" ng-options="opt for opt in allCsvFiles"></select>
then the dropdown selection worked as expected.
I may have missed some other obvious solution here, but as the array of json objects is one dimensional anyway it doesn't seem to be an issue.
It looks like the OPs question has been answered, I just thought I'd add this as it solved it for me.

Resources