Knockout - How to bind outer container css from set of checkboxes? - javascript

OK, admittedly horrible grammar in the question, but here's the lowdown:
I'm using the following in my site:
Twitter Bootstrap
Knockout
Durandal
I'm trying to add a css class to the outer label surrounding a checkbox (in a list of checkboxes) so it can highlight the selected checkboxes. Essentially the code is this:
<div data-bind="foreach:values">
<label class="checkbox inline btn" data-bind="css: { }">
<input type="checkbox" data-bind="attr: { value: text }, checked: $parent.checkedValues" />
<span data-bind="text: text"></span>
</label>
</div>
So, what I'm trying to do is to add the btn-primary class to the outer label for the checkboxes that are checked. Rather than put the full viewmodel in here, I've created a Fiddle: http://jsfiddle.net/riceboyler/WEPRZ/1/
I recognize that I can use the $data object to get the current item, but I can't figure out how to check and see if the current item ($data.text) is in the checkedValues observableArray. I'm sure it's probably basic Javascript that I'm missing, but is there any way to do this without using a computed value?

You can do this directly in the css binding by inspecting the parent's checkedValues array.
<label
class="checkbox inline btn"
data-bind="css: {'btn-primary': $parent.checkedValues.indexOf(text) > -1}">
See the Fiddle

You can try this predicate :
$parent.checkedValues().indexOf($data.text) >= 0
I created a new version your fiddle.
I hope this helps.

Related

Angular ng-repeat with bootstrap input checkbox with dynamic ng-model using ng-true-value

This problem is a little tricky for me to explain so I'll do my best. I'm trying to successfully implement Angular checkbox with their ng-true-value correctly with a dynamic ng-model.
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<div ng-repeat="asset in asset">
<h5>{{ asset.title }} categories are...{{ asset.category[0] + " and " + asset.category[1] }}</h5>
<div class="form-group">
<label>Business</label>
<div class="checkbox" ng-repeat="category in categoryFactory.business">
<input type="checkbox" id="{{category}}" ng-true-value="{{'category'}}" ng-model="asset.category[$index]">
<label for="{{category}}">{{category}}</label>
</div>
</div>
<div class="form-group">
<label>Personal</label>
<div class="checkbox" ng-repeat="category in categoryFactory.personal">
<input type="checkbox" id="{{category}}" ng-true-value="{{'category'}}" ng-model="asset.category[$index]">
<label for="{{category}}">{{category}}</label>
</div>
</div>
</div>
</div>
My end goal is when I load up my asset (find below...) it will display like the picture here My end goal, with the correct checkboxes checked. The Business and Personal categories are derived from a factory and each form-group checkbox's are from an ng-repeat list in Business / Personal in the factory.
$scope.asset = [{
title: "Sales Agreement",
category: ["finance-admin", "running-a-business"]
}];
My issue is having to correctly implement ng-model inside the checkbox. Since each asset can have 1 or more category it needs to be dynamic. I have tried using $index to make my ng-model dynamic...
ng-model="asset.category[$index]"
But that seems to not work like I would have hoped. I am probably missing something very easy and some help would be greatly appreciated.
Thanks!
By the way here is the
JSFIDDLE
Currently as per your $index logic the two arrays categoryFactory.business and asset.category must be of same size so here is the updated jsfiddle
but the main part is this
First, for your case ng-true-value should be an expression
ng-true-value="{{category}}"
Second, your asset.category array indices should match with your options in categoryFactory.business array
$scope.asset = [{
title: "Sales Agreement",
category: ["", "running-a-business","", "","finance-admin"]
}];
There could be a better option to manage this without relying on the $index but this should do for your case
I used ng-checked, you can put function as an attribute value, ng-repeat will evaluate the function in each loop.
<input type="checkbox" id="{{category}}" ng-checked="isCategory(category)" >
$scope.isCategory = function(category){
return $scope.asset[0].category.indexOf(category) >= 0;
}
In isCategory() we are checking the indices of the category in asset.category array, if it finds it will true.
https://jsfiddle.net/pg5dqgr9/8/

Cannot set a value in $scope in Angular?

I am following the tutorial on Lynda.com for AngularJS essential training.
Part of my index file looks like:
<div class="container" ng-controller="AppCtrl">
<h1>AngulAir</h1>
<ul class="nav nav-pills">
<li role="presentation" ng-class="destinationsActive">Destinations</li>
<li role="presentation" ng-class="flightsActive">Flights</li>
<li role="presentation" ng-class="reservationsActive">Reservations</li>
</ul>
<div ng-view>
</div>
<p>{{ flightsActive }}</p>
</div>
Now when I click on any link it should fire the setActive function defined in the AppCtrl which looks like this:
$scope.setActive = function (type) {
$scope.destinationsActive = '';
$scope.flightsActive = '';
$scope.reservationsActive = '';
$scope[type + 'Active'] = 'active';
};
Now the problem is very simple. The function should take the type for example 'destinations' and append 'Active' to it and set the scope variable 'destinationsActive' to active which in turn should be reflected in the ng-class directive of the li tags and the link should be active.
I have tried to insert alert('hello'); after setting it active which fires up. Now this means that the function is indeed being called. But when I do alert($scope.destinationsActive); it gives me a blank alert whereas it should give me active as the value.
I am not following with the exercise files and I feel that maybe because the tutorial is relatively older, there might be changes in the framework. I have already encountered such problems with the tutorial. Anyway, what is it that I am doing wrong?
In your ng-click directives you are passing the argument as a variable, not a string.
ng-click="setActive(destinations)"
Will pass in the value of the $scope.destinations, which is undefined. Try passing in a string i.e.:
ng-click="setActive('destinations')"
Note the single quotes
You need to put parenthesis around the parameters in your html javascript function call.
Destinations
Here is a working example: JSFiddle
I would recommend you to think of the model binding in more semantic way. For example use a checkbox instead of link and set the checkbox value as ng-model to a scope variable instead.
<input type="checkbox" ng-model="destinations">
<input type="checkbox" ng-model="flights">
<input type="checkbox" ng-model="reservations">
and thereafter use the model to reflect the rest of the changes (this is the answer to the original question, you have to specify a classname and a condition in brackets:
<li ng-class="{ destinationsActive: destinations }">
Pass argument as string in ng-click.
working code : http://jsfiddle.net/Virbhadrasinh/cLsLfkjm/

Angular not parse the template {{value}} in style attribute in IE9 and 10

I display a list of bars in a NgRepeat and I use the value frecuency to display the width of bars in percentage. From what i see IE 9-10 doesn't like this part: style="width:{{type.frecuency}}%;"
<div class="drp" ng-repeat="type in weeks">
<div style="width:{{type.frecuency}}%;" class="percentBar">
<span ng-if="type.frecuency > 14">{{type.frecuency}}%</span>
</div>
</div>
Is this an issue with Angular on IE or my code is the problem.
Thanks
P.S.
I know that i could make a class but modifying the style attribute is faster.
Solution: ng-style="setBarWidth(type.frecuency);"
scope.setBarWidth = function(width) {
return {width: width+'%'};
};
When using derived values for various HTML attributes, it's always a good idea to use the provided Angular directives to do it. They make sure that the browser sees the values you want it to see and not the binding syntax (in your case {{type.frecuency}})
Here, the ngStyle directive should be used.
<div class="drp" ng-repeat="type in weeks">
<div ng-style="width:{{type.frecuency}}%;" class="percentBar">
<span ng-if="type.frecuency > 14">{{type.frecuency}}%</span>
</div>
</div>
There are similar directives for many other HTML attributes, see the documentation for the full list.

Bind ng-models in input type checkbox

I have a problem when binding ng-models with ng-repeat in a input tag type checkbox.
I will first attach my code and then explain more in detail.
app/main.html:
<div ng-repeat="feature in features">
<input type="checkbox" ng-model="features[$index].name">{{features[$index].name}}
</div>
<br></br>
<div class="highlighter">
<span ng-class="{emo:Emotions}">Manually</span> <span ng-class="{feel:Feelings}">create</span> the <span ng-class="{emo:Emotions}">entire</span>
</div>
main.js
angular.module('webClientApp')
.controller('MainCtrl', function ($scope,$http) {
[...other variables...]
$scope.features = [{'name':'Emotions'},{'name':'Feelings'}];
[...other parts of code]
});
Let's also assume that in the main.css file there are references to the classes .emo' and.feel' respectively to highlight the target word when the user ticks the box relative to the feature.
Now, the application works correctly when I listed all the inputs one by one like the following:
<input type="checkbox" ng-model="Emotions">Emotions
<input type="checkbox" ng-model="Feelings">Feelings
but I wanted to wrap it into an ng-repeat and list the features in the controller scope, since the features I will considered will be more. When I try the code above when I tick on the box the name changes to `true'.
I have read a lot about how to bind models to an ng-repeat inside a input tag but none of the solutions apply to my case.
Can someone please help me?
I changed thigs up quite a bit from your original model but... I did get something to behave similar to what you are looking for.
HTML
<div ng-app="webClientApp">
<div ng-controller="MainCtrl">
<div ng-repeat="(feature,enabled) in features">
<input type="checkbox" ng-model="features[feature]">{{feature}}</input>
</div>
<div class="highlighter">
<span ng-class="{emo:features.Emotions}">Manually</span> <span ng-class="{feel:features.Feelings}">create</span> the <span ng-class="{emo:features.Emotions}">entire</span>
</div>
{{features}}<br>
{{features.Emotions}}<br>
{{features.Feelings}}
</div>
JS
var app = angular.module('webClientApp', []);
app.controller('MainCtrl', function($scope, $http) {
$scope.features = {Emotions: true, Feelings: true};
});
Here's the fiddle: http://jsfiddle.net/rodhartzell/8YrxQ/
Hope this helps.
(i should add this as a comment, but I don't have enough rep. yet)
There is an issue on github which concerns your issue: https://github.com/angular/angular.js/issues/1404 and the comment of caitp shows some workarounds: https://github.com/angular/angular.js/issues/1404#issuecomment-30859987
You could (also) define a new javascript object in your controller and map the elements to that.
In controller: $scope.awnsers = {};
In template: ng-model="awnsers[feature.name]"
I hope this helps
You must use ng-checked instead of ng-model.
Check out this jsfiddle
http://jsfiddle.net/fizerkhan/z5z9s/24/
ngModel and ngChecked are not meant to be used together.
ngChecked is expecting an expression, so by saying ng-checked="master". If the expression is truthy, then special attribute "checked" will be set on the element
You should be able to just use ngModel, tied to a boolean property on your model. If you want something else, then you either need to use ngTrueValue and ngFalseValue (which only support strings right now), or write your own directive.

KnockoutJs - handling several areas with the same fields

I have several regions with repeatable content which is generated on the server-side. I use knockout-js to dynamically hide/show regions within areas. My markup is like the following:
<div>
<input type="checkbox" data-bind="checked: a1" />
<div data-bind="visible: a1">region0</div>
</div>
<div>
<input type="checkbox" data-bind="checked: a2" />
<div data-bind="visible: a2">region1</div>
</div>
<script>
var viewModel = {
a1: ko.observable(false),
a2: ko.observable(false)
};
ko.applyBindings(viewModel);
</script>
Lets say I have 10 such regions. Is there a more convenient/better way to achieve the same?
Lets say, that I explicitly do not want to use foreach binding and generate markup on the client (for site to be accessible with disabled js).
Is there any way to omit viewModel specification (part within script tags), since it feels to me that knockout could detect and auto-create fields for me?
P.S. I'm a JS-novice, so excuse me for simple questions :)
Is there any way to omit viewModel specification (part within script tags), since it feels to me that knockout could detect and auto-create fields for me?
Although I prefer Knockout personally, you might want to take a look at Angular. Angular does automatically create view model properties as this example shows.

Categories