Understanding two-way data binding tutorial in AngularJS official site - javascript

I am looking at the official tutorial on AngularJS website that explains two-way data binding.
https://docs.angularjs.org/tutorial/step_04
The tutorial mentions this:
Angular creates a two way data-binding between the select element and
the orderProp model. orderProp is then used as the input for the
orderBy filter.
However, when looking at the live demo I only see one-way binding.
Can anybody explain how that demo is supposed to illustrate two-way data binding?

The tutorial has this explanation (emphasis mine):
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the browser, "Newest" is selected in the drop down menu. This is because we set orderProp to 'age' in the controller. So the binding works in the direction from our model to the UI. Now if you select "Alphabetically" in the drop down menu, the model will be updated as well and the phones will be reordered. That is the data-binding doing its job in the opposite direction — from the UI to the model.
So this is the demonstration of two-way binding. Although not very obvious.

Well, what you see is the binding from the view to the model (you write something in the input box and the view changes).
The other way round binding (from the model to the view) is the 'standard' javascript binding: you set a value in a scope variable (linked by Angular to a DOM element), and the view reflects it...
I agree with #Sergio Tulentsev it's not so obvious... :-)

//ng-model="query" is two way data binding of input to model property query
// any change in model will reflect inside input textbox and vice versa
Search: <input ng-model="query">
//ng-model="orderProp" will assign selected option to model property orderProp
Sort by:
<select ng-model="orderProp">
<option value="name">Alphabetical</option>
<option value="age">Newest</option>
</select>
<ul class="phones">
//filter:query will filter phones having its property matching words to query and result will be ordered by orderProp that is by name or age
<li ng-repeat="phone in phones | filter:query | orderBy:orderProp">
<span>{{phone.name}}</span>
<p>{{phone.snippet}}</p>
</li>
</ul>

Related

Converting md-switch to md-select

I'm working on this dynamic filter system using AngularJs and trying to figure out how to convert the color and size options to be in two dropdowns (one for each category).
I've tried the following code which does successfully add the dropdown along with the options in the select box, but upon click of one of the options, it doesn't select it.
<md-select ng-model="filter[cat][value]" placeholder="val" multiple>
<md-option ng-repeat="value in getItems(cat, data)" ng-value="{{value}}" ng-init="filter[cat]={}">
{{value}}
</md-option>
</md-select>
Demo: https://codepen.io/mikelkel/pen/rwQKRm (Above code had been removed from this demo.)
There are a few issues:
According to the documentation, multiple is a boolean so it needs to be multiple="true".
value in ng-model doesn't exist in that scope since it only exists in md-option, so you can't do filter[cat][value]. If you look at the documentation for md-select you will see that when using multiple the model is an array. So if you set your ng-model to simply filter[cat] then its value will be something like ["red","yellow"].
You can then modify filterByPropertiesMatchingAND so that it does a string match against like properties (so if the shirt color is red and the filter.color array contains red then you would return true).
I forked your codepen (https://codepen.io/czema/pen/KqbpPE) and made the following changes:
Even though ng-init is frowned upon I left it, but I initialized the filter[cat] to an array rather than an object.
I removed the md-item.
I set ng-model to filter[cat] and multiple="true"
In the Javascript I modified your filterByPropertiesMatchingAND function. Now it expects $scope.filter contain arrays for each property (rather than an object with color names and boolean values). I dropped the noSubFilter function.
Use ng-options in select rather than ng-repeat on the option tag. The syntax for ng-options is a little odd, but it would be something like this in your case: ng-options="value for value in getItems(cat, data)"
Read the documentation for ngOption https://docs.angularjs.org/api/ng/directive/ngOptions
As far as why your attempt above doesn't work, probably has something to do with the ng-init, which is being executed for each option and is probably wiping out any existing data. Don't use ng-init.

Properly separating the data model and view model

I am asking this in the context of AngularJS, but this question could really be applied to any language. So in a web application we have a use case where we need to fetch some data from a server (HTTP request) and then display that data in the UI.
So lets say our application is displaying a list of Books. Our process would look like this:
Run a GET request to /api/books to get a list of all our books
(optional) Transform the server-data-model to a client-data-model if needed
Bind the models to the $scope so they are accessible via the view
Iterate through the models on the $scope and display them in the HTML
At this point lets say we have a simple list of checkboxes with a book title next to it like this:
<ul>
<li ng-repeat="book in vm.Books">
<input type="checkbox" ng-model="<HERE>" name="my-books" />
<label>{{book.title}}</label>
</li>
</ul>
As you can see, in this template we reference the book.title in order to display it in the page. However, you can also see that the ngModel is unknown. This is where I am not sure what to do. The simple solution is to just tack on a UI model for use in the UI. This means that in step 2 above we would do book.UI = {} to every single model, then when we need to send that model back to the server we would have to do delete book.UI to clean it back up.
Doing this would allow our template to look like this now:
<ul>
<li ng-repeat="book in vm.Books">
<input type="checkbox" ng-model="book.UI.isSelected" name="my-books" />
<label>{{book.title}}</label>
</li>
</ul>
Now we can control when a book is selected via the checkbox input. This work OK, but it doesn't separate our concerns enough and there are side effects to using this pattern.
I'm sure there is a abstract design pattern that could solve this that isn't implementation specific, I just am not aware of any myself. Does anyone have any advice on how to obtain this kind of flexibility in the front end but separates our view models and data models completely so we don't have to do any "clean up" work?
You can use book.title as a dynamic object key. By default, the key value will be undefined, and it is set to true when the box is checked. If unchecked, the value will be set to false.
var checkedBookTitles = {};
<input type="checkbox" ng-model="checkedBookTitles[book.title]" />
If book.title = 'Javascript', then checkedBookTitles['Javascript'] = true when the proper checkbox is checked, it becomes false when unchecked.

Angular Drop Down, Select by Index

I am retrieving an unordered list and binding to a drop down here, but on instantiation I want to be able to select the 0th item in the drop down. ng-init="selectedTreatment=treatments[0]" doesn't work, as the list is reordered in the view.
<select ng-model="selectedTreatment"
ng-options="option.TreatmentName for option in (treatments | orderBy : 'TreatmentName')">
</select>
Is there an Angular way to do this?
In tour controller set the select ng-model
$scope.selectedTreatment = treatments[0].TreatmentName
One solution was to order the data in the controller, the same way as it is ordered in the view. Then in the controller I injected the $filter into the controller and did:
$scope.selectedTreatment = $filter('orderBy')($scope.treatments, 'TreatmentName')[0];
This allowed me to utilise the orderBy filter in the controller. This fixed my problem, but seems like it doesn't offer full SOC. A way to do this neatly in the view doesn't seem to be possible.

Angular populate ng-repeat and ng-model

In my app I have a widget that is used on multiple pages and the selections made on the widgets are to be transferred between pages. So if I select something in the widget on the homepage and click 'continue' which then takes me to a new page, the widget should be pre-populated with the selections made on the homepage.
My widget follows the following format (although not exact for simplistic explanations):
<li ng-repeat="device in devices track by $index">
<select name="model[{{$index}}]" ng-model="selectedManufacturer" ng-options"...">
<select name="model[{{$index}}]" ng-model="selectedModel" ng-options"...">
</li>
It's the values of the ng-models inside the repeat statement that contain the key data in the widget that needs to be transferrable.
The number of devices needs to be transferrable too but that will be easy as devices is just an array of numbers so selectedModel.length -1 will equal the value of devices on the secondary pages.
Is it possible to pre-populate the values of ng-models?
Yes, you can prepopulate values of ng-models (or any scope variable) using ng-init
If you want to pre-populate the value in your ng-model which would mean a default/inital value in your drop-down. This can be achieved by doing the following:-
$scope.selectedManufacturer = listOfManufacturer[0];
$scope.selectedModel = listOfSelectedModel[0];

Multiple bindings for select angular

I'm new to angular and I'm finding that working with a select to be a bit tricky. I'm creating a select element:
<select ng-model="userToAdd.CollegeCode">
<option ng-repeat="college in colleges" value="{{college.CollegeCode}}">{{college.CollegeName}}</option>
</select>
in ng-model I was able to bind to userToAdd.CollegeCode, but I would also like to bind the text to userToAdd.CollegeName. So far I'm unable to find an example of this. Second, the select is bound to userToAdd.CollegeCode when the object is updated the select option does not select that value. It always starts at the first element. Can someone point me in the right direction?
You should have a $scope.selectedCollege property in your controller, so when a code is selected you can set the corresponding college object as the currently selected object (not sure if you have to iterate all your college objects and find the one, I don't have any code - i would put an ng-click="selectCollege($index)" on the option).
That way you have all possible properties of a college in hand not just the code or the name.
That should resolve both of your issues, as you would retrieve the college name of the newly selected object from $scope.selectedCollege.name.

Categories