Angular JS Text Field Input Change - Watch - angularjs

I have a list of text fields with an empty text field at the end, bound to an array. I need to detect text entry on the empty text field so when a user starts typing a value, I add another empty element to array so the user always has another field ready to work with.
Should I use $watch or ng-change to see the change go down and add the element accordingly? I know $watch is always firing so it seems like that may be a bad option.
<div ng-repeat="variation in productEditorModel.ColorVariations">
<div class="form-inline">
<input type="text" id="txtVariationName" placeholder="Name" name="variationName" ng-model="variation.VariationName" required class="form-control">
</div>
</div>

Considering performance aspect, you should better use ng-change because it will work as you've added change listener on the input like $('input').change(...) just as you mentioned.
Considering UX and functionality aspects it is better and easier to simply use $scope.$watch('model', ...) in controller.
But still I suppose it depends on how many inputs you will have. I think there is no really big difference with even 100+ inputs because you just comparing strings, I don't think that user will struggle with delays as he types.

Why don't you bind the array to an ng-repeat of input elements? This way the binding will work automatically.

thanks for the input, ng-change worked best.
ng-change="update(variation,$index);"
$scope.update = function (variation, index) {
if (!angular.isUndefined(variation.VariationName)) {
if (variation.VariationName.length > 0) {
$scope.addVariation(variation.VariationTypeId);
} else {
$scope.productEditorModel.ColorVariations.splice(index, 1);
$scope.removeVariation(index, variation.VariationTypeId);
}
} else {
$scope.removeVariation(index, variation.VariationTypeId);
}
}

Related

ng-model preventing ng-value to be displayed

I'm pretty new to angular world and I have an issue with it.
I'm working with ejs too.
I have an input that I want to fill (value) with an ng-model.
The problem is my model is empty while the user doesn't specify a value.
I want to display a default value when my model is empty. This default value is sending by the ejs (server side). Doing that, I can't set a default value in my controller.
To do so I wrote the following :
<input type="text" ng-model="owner_adress" ng-value="'{{owner_adress || '<%=user.owner_adress%>'}}'"/>
If I look into my code, I can see the value is okay (ejs result when my model is empty, my model value otherwise) but the value is not displayed in my input (ie the user can't see it).
I looked for a work around (ng-cloak was fine but I can't use it in my input field).
Any clue would be nice !
Use ngInit directive instead. If owner_adress is defined in controller it will be used, otherwise it will default to serverside rendered value:
<input ng-model="owner_adress" type="text"
ng-init="owner_adress = owner_adress || '<%=user.owner_adress%>'"/>

Angular radio button scope

I'm working on a radio button list where a user can select from a pre-populated list of problems, or select an "other" radio button and then type in their specific problem.
I can get the pre-populated list of radio buttons to work and set the problem (outputting the scope variable confirms this), but introducing the "other" functionality is stumping me. When I select other, it doesn't seem to bind to the scope variable. I noticed in the dom it's missing an class="ng-scope" that the other radio buttons seem to get from the ng-repeat, but I'm not sure if that's the problem.
<form>
// This part loops through the list of problems and makess radio buttons
<div ng-repeat="problem in selectedType['nature_of_problem']">
<input type="radio" ng-model="$parent.natureOfProblem" ng-value="problem"/>
</div>
// Ideally this part is where the "other" radio is, it's still in the form
<input type="radio" ng-model="natureOfProblem" ng-value="other" ng-checked="">
</form>
Working JSFiddle:
http://jsfiddle.net/HB7LU/3794/
I saw a few issues, among them:
Using ng-value instead of plain old value for "other"
Using a primitive instead of dot notation (if you want your view to reliably write a variable, it needs to be something.yourVariable instead of just plain old yourVariable)
Hope this helps!
function MyCtrl($scope) {
$scope.uiState = {};
$scope.uiState.natureOfProblem = 1;
$scope.selectedType = {};
$scope.selectedType.nature_of_problem = [1,2,3];
}
<div ng-controller="MyCtrl">
<p>Nature of problem is: {{uiState.natureOfProblem}}</p>
<form>
<div ng-repeat="problem in selectedType['nature_of_problem']">
<input type="radio" ng-model="uiState.natureOfProblem" ng-value="problem"/><span ng-bind="problem"></span>
</div>
<input type="radio" ng-model="uiState.natureOfProblem" value="Other" /><span>Other</span>
</form>
</div>
EDIT to answer OP's questions:
I tend to use ng-bind out of habit -- in slower browsers like Firefox, it keeps "{{blah}}" from showing up on the screen as everything loads. Newer versions of Angular also have ng-cloak for this purpose, which I should probably get in the habit of using instead. :) (I also vaguely remember reading that "{{blah}}" can cause issues in IE, but I very possibly made that up.)
The use of dot notation relates to the fact that Angular can't maintain data bindings on brand-new objects. To try to explain it without using terms like "scope" and "inheritance": If you influence an existing object by changing yourObject.anAttribute, the overarching object consistently exists throughout that process and does not drop its binding. But if you have blahVariable that is equal to 8, and you set blahVariable equal to 7, you've basically tossed the old piece of data and created a new piece of data entirely. This new piece does not maintain the binding, so the controller never gets the memo from the view that the value has changed.
Sometimes I find this useful, actually -- you can briefly manipulate a variable in the view for some quick-and-dirty purpose without the controller finding out about it. :)

Ambiguity in the use of ngRepeat

I have following problem with using of AngularJS ngRepeat.
The issue can be viewed in this jsFiddle.
http://jsfiddle.net/zono/9rmEs/2/
The user can choose character and after this get all combination
of chosen characters in alphabet. Eg:
A - A-B, A-C, A-D and etc.
B - B-A, B-C, B-D and etc.
Everithing works properly but when user change value of selected
character the combination does not get updated. I solved this problem
with adding following code.
<span style="display: none;">
{{item.combionations = getCombinations(item)}}
</span>
And "hack" it. But there must be normal solution.
I would be very grateful for any ideas and recommendations.
Best regards.
Update
In case you plan to do more complex calculations based on the selection this simplified approach would not work. In general it is also better to encapsulate state in some data structure. In your case you could design a structure like this:
{ letter: "A", combinations: ["A-B", "A-C", ... ] }
To update the combinations array you can use ng-change="updateItem(item)" and some update function. Whenever you change the selection the array combination gets updated:
$scope.updateItem = function(item) {
item.combinations = getCombinations(item.letter);
}
I put this in a new fiddle.
You can easily solve this issue by using the model you bound to ng-select in the ng-repeat.
In the select you used item.model. Angular will update its value in the scope whenever you change the selection.
<select data-ng-model="item.model" ng-init="item.model=allLetters[0]" ng-options="value for value in allLetters">
</select>
When you use the same scope variable in ng-repeat you should get the desired behavior.
<div ng-repeat="letter in allLetters">
{{item.model}}-{{letter}}
</div>
Take a look an the updated fiddle.
The problem is that you compute combionations once at the begenning (ng-init="item.combionations=getCombinations(item)"). After that it never gets updated when you change item.model.
You could solve this problem (and also make sure created[...].combionations is kept up-to-date) like this:
<div data-ng-repeat="item in created">
<div ng-repeat="combination in item.combionations = getCombinations(item)">
{{combination}}
</div>
...
See, also, this short demo.

AngularJS - Reset form using $setPristine() when all controls are emptied

I'm trying to create a reusable directive that will simply reset my form and it's child controls to pristine using the $setPristine() method IF all the form's input controls are emptied after the user has previously interacted with them and marked them dirty.
So basically the directive would monitor all the form <input> elements and if it determines all elements are empty call $setPristine() to reset everything back to square one.
This seems kind of trivial and something I could bust out with jQuery in 5 mins but I'm just getting my feet wet with Angular and I've been stumbling around this for a couple of hours struggling with the best approach so any help or guidance is greatly appreciated!
Edit, easier answer: Use the require or ng-require attribute on the form elements, keeping the form $pristine if there is an error.
If require is not wanted:
Note - you need angular version 1.1.x for $setPristine().
Assuming all of the ng-model's in the form are properties of the same object, you could $watch the object, loop through the properties to see if they are undefined or '' empty strings, and $setPristine() if they are.
Form HTML - all of the models are properties of input object:
<form name="form">
<input type="text" name="one" ng-model="input.one">
<input type="text" name="two" ng-model="input.two"><br/>
<input type="submit" ng-disabled="form.$pristine">
</form>
In the controller or directive, $watch the model for changes, then loop through the object, seeing if all properties are undefined or ''. (If used in the link function, you would typically use scope in place of $scope.
var setPristine = function(input){
if (input === undefined || input === ''){
return 0;
}
return 1;
}
$scope.$watch('input', function(i){
var flag = 0;
//loop through the model properties
for (var obj in i){
flag +=setPristine(i[obj]);
}
// if nothing in the model object, setPristine()
if(flag===0){
$scope.form.$setPristine();
}
}, true)// true allows $watch of object properties, with some overhead
Use the dirty/pristine states of the form to know if user has touched them, and a ng-pattern to know if the field is empty or not with a regexp like /.+/. Then you can just check myForm.$dirty and myForm.$error.pattern and voila!

Dynamic data-binding in AngularJS

I'm building an AngularJS app and I have ran into an issue. I have been playing with the framework for a while and I have yet to see documentation for something like this or any examples. I'm not sure which path to go down, Directive, Module, or something that I haven't heard of yet...
Problem:
Basically my app allows the user to add objects, we will say spans for this example, that have certain attribute's that are editable: height and an associated label. Rather than every span have its own dedicated input fields for height and label manipulation I would like to use one set of input fields that are able to control all iterations of our span object.
So my approx. working code is something like this:
<span ng-repeat="widget in chart.object">
<label>{{widget.label}}</label>
<span id="obj-js" class="obj" style="height:{{widget.amt}}px"></span>
</span>
<button ng-click="addObject()" class="add">ADD</button>
<input type="text" class="builder-input" ng-model="chart.object[0]['label']"/>
<input type="range" class="slider" ng-model="chart.object[0]['amt']"/>
The above code will let users add new objects, but the UI is obviously hardcoded to the first object in the array.
Desired Functionality:
When a user clicks on an object it updates the value of the input's ng-model to bind to the object clicked. So if "object_2" is clicked the input's ng-model updates to sync with the object_2's value. If the user clicks on "object_4" it updates the input's ng-model, you get the idea. Smart UI, essentially.
I've thought about writing a directive attribute called "sync" that could push the ng-model status to the bound UI. I've though about completely creating a new tag called <object> and construct these in the controller. And I've thought about using ng-click="someFn()" that updates the input fields. All of these are 'possibilities' that have their own pros and cons, but I thought before I either spin out on something or go down the wrong road I would ask the community.
Has anyone done this before (if so, examples)? If not, what would be the cleanest, AngularJS way to perform this? Cheers.
I don't think you need to use a custom directive specifically for this situation - although that may be helpful in your app once your controls are more involved.
Take as look at this possible solution, with a bit of formatting added:
http://jsfiddle.net/tLfYt/
I think the simplest way to solve this requires:
- Store 'selected' index in scope
- Bind ng-click to each repeated span, and use this to update the index.
From there, you can do exactly as you proposed: update the model on your inputs. This way of declarative thinking is something I love about Angular - your application can flow the way you would logically think about the problem.
In your controller:
$scope.selectedObjectIndex = null;
$scope.selectObject = function($index) {
$scope.selectedObjectIndex = $index;
}
In your ng-repeat:
<span ng-repeat="widget in chart.object" ng-click="selectObject($index)">
Your inputs:
<input type="text" class="builder-input" ng-model="chart.object[selectedObjectIndex]['label']"/>
<input type="range" class="slider" ng-model="chart.object[selectedObjectIndex]['amt']"/>

Resources