Update binding after server response / How do I selectively update bindings? - angularjs

I'm trying to prevent the value of a model from updating in a form.
For example :
I have a payment details form that lists the user's saved information (name, address etc) along with the form that is used to edit the same information.
I've been experimenting using the :: for one time binding as I don't want the displayed information to changed when input controls are changed (but I obviously want the models updated values so i can send them to the server for processing).
How do I update the displayed model values after the server responds that the changes have been saved, are ok etc? I can't seem to find a way to update the one time binding (as I'm guessing this is fully the intended functionality).
So I guess my question boils down to :
How do I selectively update bindings on some controls but not others?

Actually you just want to display different vars.
You should try with a temporary model object (that is a copy of your object like "editedObject") and when you validate you will update the original object.
See it working in this plunker
The editing space :
<input ng-model="editCopy.value"> <button ng-click="validateChange()">Change</button>
The ng-repeat :
<td ng-repeat="item in items" ng-click="editItem(item)">
{{item.value}}
</td>
The functions :
$scope.editItem = function(item){
$scope.editCopy = angular.copy(item);
$scope.editingItem = item;
}
$scope.validateChange = function(){
$http.get('index.html').success(function(){
$scope.editingItem.value = $scope.editCopy.value;
});
}

Related

Angularjs : creating dynamic form

I'm developing a friend invitation feature for a website.
Only requirements are : by email and has a max number of invitations at a time.
My idea is the following :
At the start, user only sees one email field. When he enters an email adress in the only field, angularjs validates it (email format check) and creates an additional email field.
Now, I come from a jquery background and I think it's bad practice to manipulate DOM with angular.
How would one do it with angularjs ?
Is it a good idea to create a factory that "produces" (from a template file) fields ?
Can a library like bootstrap ui help me write simpler code for form validation and error management
This Plunker might fulfill your need at its closest: http://plnkr.co/edit/5qRXQ1XGzUnhYjLCiyYR?p=preview
The key point in this technique is letting the user directly edit a dynamic list of models. Indeed in the example, $scope.invites contains your values. The trick here is referring to them as models:
<input type="email" class="invite" name="invite{{ $index }}" ng-model="invites[$index].mail" ng-change="checkInvite($index)" />
$index being the index of the current ng-repeat iteration. checkInvite function will take care of watching changes in your invites fields.
Notes:
invites is an array of objects, this way we're sure not to mess with ng-repeat, iterating over the reference that we handle (vs models that would be handled by angular)
The field's name is useful to manually check the field's validity: in the controller we can check a field's validity accessing $scope.formName.fieldName.$valid
I also added an extra test that checks if the user clears a non-last filled-in field. In this case, we remove the field.
Have fun using angular!
Personally, I would find the design confusing, since I wouldn't know I could have more email addresses. At the minimum, I would want a + to indicate to the user that s/he can add more addresses. Think of how airlines do "multiple destinations" searches on their Websites.
However, if you are set at this, use an array in the scope. I am using a table for this, but anything will do.
<input ng-model="newemailaddress"></input><button ng-click="addEmail">Add</button>
<table>
<tr ng-repeat="addr in addresses"><td>{{addr}}</td></tr>
</table>
And your controller something like:
.controller('MyCtrl',function($scope) {
$scope.addresses = [];;
$scope.newemailaddress = "";
$scope.addEmail = function() {
// do validation
if (valid) {
$scope.addresses.push($scope.newemailaddress);
$scope.newemailaddress = "";
};
};
})

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

Initializing ngModel with data - AngularJS Bootstrap bs-select

I am maintaining a site that allows users to create a profile of sorts that will allow them to broadcast activities to a feed. I implement ng-grid to keep track of all the profiles that are created, and have created two buttons that allow users to create/edit these profiles. My only problem right now is, when users select a row on the grid and attempt to edit that specific row, the drop-down menu is not auto-populated with the data from ngModel.
This is the part of the form I am having trouble with:
<select ng-model="source.canSendTo" ng-options="value.name for value in sourceCanSendTo" data-style="btn" bs-select></select>
And within the controller, I have sourceCanSendTo defined as:
$scope.sourceCanSendTo = [ {"id":"abc", "name": "ABC"}, {"id":bcd", "name": "BCD"} ... ];
On row selection, I simply set source = the selected item, and console.logs show that all the data is there. The other parts of the form are being populated properly (mainly s), and console.log($scope.source.canSendTo) shows that the original data is there, it's just that select is defaulted to being blank...how would I go about trying to pre-select certain elements on the drop-down select I currently have?
For example, if the profile has 'abc', 'bcd' selected, how can I make it so that when I edit that profile, the drop down box shows 'abc,bcd' instead of just "Nothing Selected"?
Edit: I previously responded to a comment inquiring about bs-select, saying that it simply controlled some CSS elements of the drop down box - seems like this is completely incorrect after a quick google search when everything else led to dead ends. Does anyone have any idea how to properly initialize the model with data so that when I preload my form, the 'can send to' drop down select actually has the selected options selected, as opposed to saying "Nothing Selected"? Thanks in advance for all help!
As you are binding source.canSendTo to the name (value.name) of sourceCanSendTo then you just need to initially have an structure binding the names which had been saved, something like this:
source.canSendTo = ['abc', 'bcd']; //And all the selected values
So you need to construct your source.canSendTo property to this structure.
PS: If you show how you bring your data from the server, I can help you to construct the source.canSendTo property.
$scope.canSendTo must be initialized with a reference to the selected option.
var initialSelection = 0;
$scope.source = { canSendTo : [ {"id":"abc", "name": "ABC"}, {"id":bcd", "name": "BCD"} ... ] };
$scope.canSendTo = $scope.source.canSendTo[initialSelection];
Finally found out what was wrong with my code - seems like the data being stored in the model wasn't the same as what was in ngOptions, played around a bit with ngOptions and managed to get something that works. Working snippet of code:
<select ng-model="sendTo.name" ng-option="value.name as value.name for value in sourceCanSendTo" data-style="btn" multiple bs-select>
(Realized that the variable being used for ngModel was a fairly ambiguous in terms of naming convention, changed it)

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.

web2py - Dropdown menu

I'm trying to have a dropdown menu for the user to select the database table. I have defined few tables in db.py and I want the user the to select a particular table from a dropdown menu and insert entries. Right now I use SQLFORM:
def index():
form=SQLFORM(db.selectedtable) #want to change the table name#
if form.process().accepted:
response.flash = 'form accepted'
elif form.errors:
response.flash = 'form has errors'
else:
response.flash = 'please fill out the form'
return dict(form=form)
I need the user to select 'selectedtable' value from a dropdown list that shows all the available tables in the DB. I do not necessarily want to retrieve the table values from DB. I am OK with defining a list with the available tables and the dropdown menu can pull the table names from that list.
So far I only found IS_IN_DB to automatically create a dropdown and PluginDropdown() but that does not serve my purpose. If soemebody can direct me to the proper way of handling this task I'd be really thankful.
Regards.
Update:
After Anthony's suggession I tried the following with , as I'm not that familiar with JS.
{{extend 'layout.html'}}
{{select='NONE'}}
<form>
<select>
{{for item in TOOLS:}}
<option value="{{select=item}}">{{=item}}</option>{{pass}}
</select>
<input type="submit" value="Go!"/>
</form>
<h2>Input form</h2>
{{=form}}
<h2>{{=select}}</h2>
As you might see this doesn't work properly. What I tried to do is to get the user chose value to 'select' variable. But it doesn't work. It always gets the last element in ITEMS (this list is defined in db.py). My next option would be to be call another controller function, passing the user selected value as an argument. Then it can prepare the form with the passed value and send to a view to display
<h2>Input form</h2>
{{=form}}
But I'm not sure how I can assign the user chosen value to an argument and then call another controller function with that arugument value.
If you have any suggestion how I can modify this to get the user chosen value thats very much appreciated. Thank you.
You could create a <select> element listing all the tables, and then load the form associated with the selected table as a web2py component via Ajax. In the view of the main page (e.g., /views/default/index.html):
<script>
jQuery(function() {
jQuery('#table').change(function() {
web2py_component("{{=URL('default', 'form.load')}}" + "/" +
jQuery(this).val(), target='form')
})
})
</script>
{{=SELECT('Select a table', *db.tables, _id='table')}}
<div id="form"></div>
And in a controller (e.g., default.py):
def form():
if request.args(0) in db.tables:
response.generic_patterns = ['load']
return dict(form=SQLFORM(db[request.args(0)]).process())
else:
raise HTTP(404)
Note, db.tables is a list of all the tables defined on the db connection object -- it is used in the SELECT() helper in the view to generate a <select> list of all the tables. The script in the view registers a jQuery event handler that fires whenever a different table is selected from the dropdown. The handler calls the web2py_component() function (which is in /static/js/web2py.js), which loads the form component via Ajax into the div with id="form". It appends the value of the selected table to the URL.
In the controller, the form() function checks for the db table name in request.args(0). It then sets response.generic_patterns so the "generic.load" view will be allowed (by default, generic views are only enabled for local requests). Alternatively, you could define your own "form.load" view, or even use a different extension (e.g., "form.html").
Because the form is loaded as a web2py Ajax component, the form submission will be trapped and submitted via Ajax as well, so it will not result in a full page reload.

Resources