knock out foreach binding with independent varriable - javascript

I am using knockout for binding in my html5 application.
I have one strange scenario.
One div i am binding using for loop like below
<div data-bind="foreach: oneList">
<select name="dropDown1" id="dropDown1" data-bind="options: ddList,optionsText: function(item) { return item.value;},optionsValue:function(item) { return item.key; }">
</select>
<input type="text" id="newValue" data-bind="value : oneValue"/>
</div>
Here oneList is diffent varriable and ddList is a diffent varriable,both are independent varriable.
So when actual binding happens drop down does not get binded but input text is binded because oneList.oneValue is valid but oneList.ddList is not valid
Please let me know if my question is not clear

Without seeing your view-model it is quite hard to tell, but most probably there is no ddList property defined on items in the oneList list.
Inside a foreach binding the current binding context refers to the current item from the list so if you need to "go up" in the binding context to access a property which is on the same level as your onlist then you need to use $parent (or $root to access your main view-model).
A fixed options binding would look like this:
<select data-bind="options: $parent.ddList, optionsText:... " >

Related

ng-model changes all select at once

jsfiddle
Hi.
I have a problem with my application. I have to write several selects by using ng-repeat and each of these selects must be filled with the same data.
The problem is, when the one is changed, others selects are changes to the same value - why?.
I suppose that the problem is in the ng-model - maybe I don't understand how the "hierarchy" of the ng-model works.
If the name of the ng-model is only "option" - it doesn't work!
If the name of the ng-model is "something.option" - it also doesn't
work!
If the name of the ng-model is "something.else.option" - it does work
but all selects are filled!
HTML:
<div ng-controller="MyCtrl">
<div ng-if="models" ng-repeat="m in models">
<br><label>{{m.model}} ({{m.no}})</label><br>
<select ng-model="models.m.opModel" ng-options="opt.value as opt.text for opt in options" ng-change="foo()"></select>
</div>
</div>
JS:
var myApp = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.models = [
{'no':'A', 'model':'alpha'},
{'no':'B', 'model':'beta'},
{'no':'C', 'model':'gamma'}
];
$scope.options = [
{'value':1, 'text':'one'},
{'value':2, 'text':'two'},
{'value':3, 'text':'three'}
];
$scope.foo = function(){
alert($scope.models.m.opModel);
}
}
What am I doing wrong?
Thanks a lot.
You've created a scope object called "m" which is the current child of the "models" list. So for each dropdown, you're going to have a different "m" scope object. This is what you need to bind to in your ng-model so that the dropdown is bound to its unique parent in the "models" list.
Change <select ng-model="models.m.opModel"> to <select ng-model="m.opModel" to fix the problem.
To access the value with the foo() function, you'll need to use this updated function:
$scope.foo = function(index){
alert($scope.models[index].opModel)
}
And update the <select> like this:
<select ng-model="m.opModel" ng-options="opt.value as opt.text for opt in options" ng-change="foo($index)"></select>
You're creating an ng-model called "opModel" in the ng-repeat which means you'll have three new opModels under $scope.models. This is an array you can access later using an index value to specify which of the $scope.models[].opModel you want to access.
Notice that I've changed the ng-change code to send the current $index which is basically an ng-repeat counter. So your foo() function will receive either a 0, 1 or 2 which lets us access the specific ngModel that we need to access.
You are binding to the single object models. Inside an ng-repeat the repeated is available "in scope". You probably want to change this code to:
<div ng-controller="MyCtrl">
<div ng-if="models" ng-repeat="m in models">
<br><label>{{m.model}} ({{m.no}})</label><br>
<select ng-model="m.opModel" ng-options="opt.value as opt.text for opt in options" ng-change="foo()"></select>
</div>
</div>
Look at the ng-model="m.opModel", that is what I've changed. You are now updating the value of the single item, and not inserting a new object into an array which is then reused by all the ng-repeat items (which is why all the values would update at the same time).

Angular expression inside an ng-repeat not being rendered when parent scope referenced

so I'm working on an elastic search implementation in Angular. So far so good but I'm running into a weird situation. Each result has an associated tag(s) that can be user-generated.
Here is my form & input:
<form ng-submit="search($event)" class="queryBuilder ng-pristine ng-valid">
<input type="text" ng-model="searchQuery" class="ng-pristine ng-untouched ng-valid">
</form>
The results are displaying just fine and so are the tags. The problem comes into play when I click on the tags. Initially, the issue was while inside the repeat, they didn't have access to the above searchQuery model so I simply changed it to target $parent.searchQuery and that worked but dig this, as soon as I did that the expression item.tag no longer worked. Now it's outputting the actual curly brackets and all.
<button ng-repeat="item in items" ng-click="$parent.searchQuery = $parent.searchQuery + ' {{item.tag}}'; search();" class="tag ng-binding ng-scope">Tag Name</button>
I'm very new to Angular, been using jQuery for a billion years so I apologize in advance if I'm missed something very basic. I've searched around and can find scope related material but not expressions inside an ng-repeat clashing with targeting parent scope. Any thoughts? Thanks in advance!
String literals (searchQuery here in this case) and primitives can not be referenced over the nested scopes in Angular or Javascript. Read a famous tutorial on this https://github.com/angular/angular.js/wiki/Understanding-Scopes
You used $parent which was working initially but when you attached ng-repeat it starts breaking because ng-repeat creates a new scope so now your $parent property is referencing to a different scope.
Regarding how you fix it: define an object in your controller like:
$scope.globalData = {};
Now reference your every ng-model with this object so to avoid the scope problems:
<form ng-submit="search($event)" class="queryBuilder ng-pristine ng-valid">
<input type="text" ng-model="globalData.searchQuery" class="ng-pristine ng-untouched ng-valid">
</form>
And your ng-repeat to like this:
<button ng-repeat="item in items" ng-click="globalData.searchQuery = globalData.searchQuery + ' {{item.tag}}'; search();" class="tag ng-binding ng-scope">Tag Name</button>
This will reference your search query model with a Javascript Object and Object in Javascript can be referenced by the child instances or in case of Angular, can be referenced in the nested $scope.
Just make sure you put define that gloabalData (or whatever name you like) in a top level controller like one added to <html> or <body> tag so that it's scope can be available throughout the app. And of course, you don't have to use the $parent now :-)
Also, you can clean up your ng-click expression like:
ng-click="globalData.searchQuery = globalData.searchQuery + ' ' + item.tag; search();"
You could create a method on your controller:
$scope.clickItem = function(item){
$scope.searchQuery += ' ' + item.category;
$scope.search();
};
View:
<button ng-repeat="item in items" ng-click="clickItem(item)" class="tag">foundation</button>

How to make angular child scopes refresh?

I have a template:
<div class="oss-object cp-def cp-row" ng-repeat="(key, value) in data" ng-init="viewables = getViewables(value.versions[0].objectInfo);">
<div class="cp-def">
{{value.info.name}} OssStatus: {{value.versions[0].objectInfo.status}}
, 3D-Viewables:
<select ng-change="viewableSelected(value.versions[0].urn, viewables, '3d')" ng-model="viewables.selected3d" ng-options="viewable as viewable.name for viewable in viewables['3d']">
<option value="">---Please select---</option>
</select>
, 2D-Viewables:
<select ng-change="viewableSelected(value.versions[0].urn, viewables, '2d')" ng-model="viewables.selected2d" ng-options="viewable as viewable.name for viewable in viewables['2d']">
<option value="">---Please select---</option>
</select>
</div>
</div>
And when data gets updated (the data property used in the top ng-repeat) in my controller with a new data set, it doesn't automatically refresh the child scopes in the ng-options. which are derived from the data set. Does anyone know how to refresh child scopes? I have tried calling $apply and $digest but with no success.
As I said in the comments, you could try to replace viewables['3d'] in the ng-options with getViewables(value, '3d') and have the controller return the array.
If you look at the docs for ngInit, you'll see they basically say "never use this except in nested ngRepeats".
One way to do what you want is to create a controller for each repeated item, and create a $watch on the data that changes (or expression in this case), and set viewables equal to that.
eg
function ViewablesCtrl($scope){
$scope.$watch($scope.getViewables, function(){$scope.viewables = $scope.getViewables($scope.value.versions[0].objectInfo);}
})
--
<div class="oss-object cp-def cp-row" ng-repeat="(key, value) in data" ng-controller="ViewablesCtrl()">

ng-click doesn't take parameters from the DOM

I have the following code:
<input id="id">
<button data-action="bea" ng-click="Create($('#id1')[0].value);" class="btn">Insert ID</button>
<button data-action="bea" ng-click="Create($('#id2')[0].value);" class="btn">Insert ID</button>
In the JS I have:
$scope.Create = function (id){
if (id === undefined) {
$scope.data = "You must specify an id";
} else {
$scope.data = data;
console.log(data);
});
}
};
When the call gets into the Create function the value of the id is undefined.
If I add the following line at the beginging of the Create function everything works ok:
id = $('#id')[0].value;
If I send a constant value it works:
<button data-action="bea" ng-click="Create('SomeID');" class="btn">Insert ID</button>
Why is this happening and how can I do that without putting the line of value into the method?
Thanks
This is just an extension of comments and other answers, You could achieve this in many ways using angular, one simple example could be:-
<!-- Add a controller -->
<div ng-controller="MainCtrl">
<!-- Give a model binding to your text input -->
<input ng-model="userEntry" type="text"/>
<!-- ng-click pass which ever argument you need to pass, provided it is an expression that can be evaluated against the scope or any constants -->
<button data-action="bea" ng-click="Create(userEntry);" class="btn">Insert ID</button>
<!-- Some simple data binding using interpolation -->
{{data}}
<!-- Just for demo on repeater on a list of items on the scope -->
<div ng-repeat="item in items track by $index">{{item}}</div>
</div>
Example Demo
My 2 cents on the lines of what were originally trying to do:-
Use angular bindings instead of accessing DOM directly for getting the data, it really helps you deal with just the data without worrying about how to access or render it in DOM. If you think you need to access DOM for implementing business logic re-think on the design, if you really need to do it, do it in a directive. Angular is very opinionated on the design and when where you do DOM access.
ng-model
ng-binding
controller
all about ngmodel controller
This is not the way you should do in AngularJS. You should really think in Angular if you want to use AngularJS. Refer this post ("Thinking in AngularJS" if I have a jQuery background?)
All DOM manipulation should be done in Directive. Refer this page that I found really clear.
(http://ng-learn.org/2014/01/Dom-Manipulations/)
My guess is that $ is not bound to the jQuery function when the ng-click value is evaluated, because it is not exposed in the Angular scope.
Solutions to adress this:
expose the jQuery function in scope somewhere, e.g $scope.$ = $; in a controller.
make the Create function parameterless as you suggested, with a var id = $('#id')[0].value; at the beginning
my favorite : avoid using jQuery. If you put some data in the #id element, there's probably a more natural and AngularJS-idiomatic way of retrieving it than querying the DOM (e.g an Angular service).
In particular, if the element you're targeting is an <input> element, then use the ngModel directive to link the value to a $scopeproperty that will be accessible in the controller :
<input ng-model="inputData"/>
The JavaScript you are trying to pass as a parameter of the create function is not available in the scope of the Create function.
Try to target the element a different way.
Does that help?

Setting values on repeater scopes

I have an input within a repeater scope set up so that users can modify the value and see the calculated results on that value on that row - the intent is to have a spreadsheet like behavior.
I'd like to know what the best way to automatically populate the input box is, so that the field comes up set to a certain value with the model properly updated. I've set up a fiddle that tries to jam the number 4 into the value attribute here:
http://jsfiddle.net/BVRzh/
Where this is the template:
<div ng-app>
<ol ng-controller="TestCtrl">
<li ng-repeat="a in arr">
<input type="text" ng-model="testVal" value="4"></input>
<span>Value: {{testVal}}, Times {{a}}: {{testVal * a}}</span>
</li>
</ol>
</div>
And this is the javascript:
function TestCtrl($scope) {
$scope.arr = [1, 2, 3, 4];
}
Is there a better way to approach this?
ngInit can be useful :
<input type="text" ng-model="testVal" ng-init="testVal = a">
Example: http://jsfiddle.net/BVRzh/3/
If i understand correctly , this is the only case when you can use ngInit. Citation from documentation:
The only appropriate use of ngInit for aliasing special properties of
ngRepeat, as seen in the demo bellow. Besides this case, you should
use controllers rather than ngInit to initialize values on a scope.

Categories

Resources