I am trying to use a object property returned from a helper in handlebars
handlebars.registerHelper("calcPercentage", (newVal, oldVal) => {
const computed = round((newVal - oldVal) / oldVal, 1);
return {
computed,
className: computed.toString().startsWith("-") ? "red" : "green"
};
});
<p>{{calcPercentage this.totalOrders this.totalPreviousOrders}}</p>
Here I'd like to use the computed property, how can I achieve this?
Fixed using
<p style="display: inline-block; padding-bottom: 0;">
{{#with (calcPercentage this.totalOrders this.totalPreviousOrders)}}
{{computed}}
{{/with}}%
</p>
Try below, This one is working.
var data = {
yes: "Great Final got it"
};
hbs.handlebars.registerHelper("count", function(price) {
return data[price];
});
Hbs look like below,
<p>
{{count 'yes'}}
</p>
Check the demo link below ClickHere
Related
How to Write Condition on data which is fetching from Json to Angularjs?
Example : if user FIRM NAME exists Show else if user FULL NAME exists Show else Show REALNAME
I have a working Example of fetching data
at line number 25 <h3 class="moduletitle">Name : {{ module.realname }}</h3>
Please See that in PLUNKER
I hope i will get the working code update along with PLUNKER
I can suggest you have a function that returns the entity in which you want to display. Then using ng-show / ng-hide to display/hide the things you want.
Example:
function pseudoDecide(){
var displaythis = "";
if(/*boolean exp*/){ displaythis = "firm" }
else if(/*boolean exp*/) { displaythis = "full" }
else(/*boolean exp*/) { displaythis = "real" }
return displaythis;
}
Then <div ng-show="{{psedoDecide() === 'firm'}}>" etc etc, something like that.
With AngularJS 1.1.5+, you can use the ternary operator inside an expression. In your case, I believe you want something like:
<h3 class="moduletitle">Name : {{ module.firmname ? module.firmname : (module.fullname ? module.fullname : module.realname)) }}</h3>
If you don't want a nested ternary in your template, you could also go this route:
Somewhere in your controller:
$scope.pickName = function (module) {
var val;
if (module.firm_name) {
val = module.firm_name;
} else if (module.full_name) {
val = module.full_name;
} else {
val = module.realname;
}
return val;
};
And in your template:
<h3 class="moduletitle">Name : <span ng-bind="pickName(module)"></span></h3>
I am new to Knockout and have been trying to follow code examples and the documentation, but keep running into an issue. My data bindings printing the Knockout observable function, not the actual values held by my observable fields. I can get the value if I evaluate the field using (), but if you do this you do not get any live data-binding / updates.
Below are some code snippets from my project that are directly related to the issue I am describing:
HTML
<div class="col-xs-6">
<div data-bind="foreach: leftColSocialAPIs">
<div class="social-metric">
<img data-bind="attr: { src: iconPath }" />
<strong data-bind="text: name"></strong>:
<span data-bind="text: totalCount"></span>
</div>
</div>
</div>
Note: leftColSocialAPIs contains an array of SocialAPIs. I can show that code too if needed.
Initializing the totalcount attribute
var SocialAPI = (function (_super) {
__extends(SocialAPI, _super);
function SocialAPI(json) {
_super.call(this, json);
this.totalCount = ko.observable(0);
this.templateName = "social-template";
}
SocialAPI.prototype.querySuccess = function () {
this.isLoaded(true);
appManager.increaseBadgeCount(this.totalCount());
ga('send', 'event', 'API Load', 'API Load - ' + this.name, appManager.getRedactedURL());
};
SocialAPI.prototype.toJSON = function () {
var self = this;
return {
name: self.name,
isActive: self.isActive(),
type: "social"
};
};
return SocialAPI;
})(API);
Updating totalcount attribute for LinkedIn
var LinkedIn = (function (_super) {
__extends(LinkedIn, _super);
function LinkedIn(json) {
json.name = "LinkedIn";
json.iconPath = "/images/icons/linkedin-16x16.png";
_super.call(this, json);
}
LinkedIn.prototype.queryData = function () {
this.isLoaded(false);
this.totalCount(0);
$.get("http://www.linkedin.com/countserv/count/share", { "url": appManager.getURL(), "format": "json" }, this.queryCallback.bind(this), "json").fail(this.queryFail.bind(this));
};
LinkedIn.prototype.queryCallback = function (results) {
if (results != undefined) {
results.count = parseInt(results.count);
this.totalCount(isNaN(results.count) ? 0 : results.count);
}
this.querySuccess();
};
return LinkedIn;
})(SocialAPI);
In the <span data-bind="text: totalCount"></span>, I expect to see a number ranging from 0-Integer.MAX. Instead I see the following:
As you can see, its outputting the knockout function itself, not the value of the function. Every code example I've seen, including those in the official documentation, says that I should be seeing the value, not the function. What am I doing wrong here? I can provide the full application code if needed.
Not sure, but KO view models obviously tend to bind own (not inherited through prototypes) observable properties only. So you should rewrite your code to supply totalCount observable for every social network separately.
I'm trying to see if angularJs is useful for me to create a team-management application.
The issue I have:
I have a complex ng-class definition, being
ng-class="{'guard': ( guard.checked && day.func.indexOf('guard') != -1) }"
and it will prove to be bigger yet.
I was wondering if there is a way to have basically this:
# pseudocode, needs to be translated to js/angularJs
function getClasses(){
classes = ''
if ('guard' in user.day.func and guardCheckBox == checked){
classes = classes.append(' guard')
}
if ('f2' in user.day.func and f2CheckBox == checked){
classes = classes.append(' f2')
}
....
if ('fx' in user.day.func and fxCheckBox == checked){
classes = classes.append(' fx')
}
return(stripLeadingSpace(classes)
}
any tips on what to search, or any bits of code would be appreciated
a js-fiddle with what I have as of yet can be found here:
http://jsfiddle.net/mTJDh/1/
code from the fiddle for dead links
HTML:
Guard
<!--
this snippet applies the class 'guard' to every cell when the checkbox 'Guard' is checked
-->
<div ng-controller="MyCtrl">
<table ng-repeat="user in users">
<tr>
<td>{{user.name}}</td>
<td ng-repeat="day in user.days" ng-class="{'guard': ( guard.checked && day.func.indexOf('guard') != -1) }">
{{day.number}}
</td>
</tr>
</table>
</div>
JS
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.users = [
{name: 'PEDC',
days : [{number:'1', func:'guard'},
{number:'2', func:'guard'},
{number:'3', func:'guard'},
{number:'4', func:['guard','spoc']}
]
},
{name: 'JOVH',
days : [{number:'1', func:'guard'},
{number:'2', func:'guard'},
{number:'3', func:'spoc'},
{number:'4', func:'guard'}
]
}
];
}
CSS
.pending-delete {
background-color: pink
}
.guard {
border:solid black 1px
}
.spoc {
background-color: pink
}
EDIT:
This is the actual solution I use now:
http://jsfiddle.net/mTJDh/2/
basically:
added functions isGuard, isSpoc and isHoliday to my controller, with the day as an argument
these return true or false based on the json array.
idea gotten from here and https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D
ngClass also accepts methods defined on scope which return a boolean value. So you can do something like this:
<td ng-repeat="day in user.days" ng-class="{ 'guard' : getClass(day) }">
{{day.number}}
</td>
JS
$scope.getClass = function(day){
return $scope.guard.checked && day.func.indexOf('guard') != -1
}
I updated your fiddle:
http://jsfiddle.net/mTJDh/4/
use the ngClass as in the accepted answer:
<td ng-repeat="day in user.days" ng-class="getClasses(day)" day="day">
{{day.number}}
</td>
but this time rewrite the method getClasses to return an array.
the array contains at the end every class you wants for a specific day.
$scope.getClasses = function(day){
var classes = [];
if($scope.spoc && $scope.isSpoc(day)) classes.push("spoc");
if($scope.guard && $scope.isGuard(day)) classes.push("guard");
if($scope.holiday && $scope.isHoliday(day)) classes.push("holiday");
return classes;
}
and if you want a more generic one:
http://jsfiddle.net/mTJDh/5/
define:
var availableClasses = [
"guard",
"spoc",
"holiday"]
and use a loop:
$scope.getClasses = function (day) {
var classes = [];
angular.forEach(availableClasses, function (value) {
if ($scope[value] && day.func.indexOf(value) != -1) classes.push(value);
});
return classes;
}
I would use a directive, it was a bit hard to tell from your example which scope variables your CSS rules rely on (and what exactly the rules are), but hopefully it's enough to get started.
.directive('guardClass', [function() {
return {
restrict: 'A',
scope: {
guard: '=',
user: '='
},
link: function(scope, element, attrs, controller) {
scope.$watch(function() {
//return enough info about scope.guard and scope.user
//to know when one has changed
return ...
}, function() {
var classes = [];
if (...) {
classes.push('guard');
}
if (...) {
classes.push('f2');
}
....
if (...) {
classes.push('fx');
}
element.attr('class', classes.join(' '));
});
}
};
}])
And then in HTML
<td guard-class guard="guard" user="user" />
You feed the directive the two (or more) objects it needs to calculate the CSS classes. The directive sets up a $watch to trigger whenever whatever properties on those objects change. It then finds all CSS classes that needs to be there and puts them on the element using angular element.
This saves you from cluttering up your controller with this logic, and it saves you from having extensive amounts of logic inside your templates.
In WinJS can I bind a property getter in a listView? Say I have an object defined like this:
var MyLib = MyLib || {};
MyLib.ToDoItem = function() {
this.name = '';
this.description = '';
Object.defineProperty(this, "completed", {
get : function() {
return false;
}
});
}
MyLib.ToDoList = [];
//MyLib.ToDoList.push....add todo items
I am declaring a WinJS.Binding.Template where all of the properties are binding except the one that is defined with a property getter:
<div id="myItemTemplate" data-win-control="WinJS.Binding.Template">
<div class="titleTile">
<h4 class="item-title" data-win-bind="textContent: name"></h4>
<p data-win-bind="textContent: description"></p>
<div data-win-bind="textContent: completed"></div> <-- Renders as undefined
</div>
</div>
The "completed" property renders as undefined. If I put a breakpoint in the javascript console where I am loading the data, I can get to the completed property, but the databinding doesn't seem to like it...any ideas?
You missed one line after your getter.
get : function() {
return false;
}
, enumerable: true
By setting enumerable to true, you can make data binding works on this property.
I am trying to filter my users observableArray which has a nested keywords observableArray
based on a keywords observableArray on my viewModel.
When I try to use ko.utils.arrayForEach I get a stack overflow exception. See the code below, also posted in this jsfiddle
function User(id, name, keywords){
return {
id: ko.observable(id),
name: ko.observable(name),
keywords: ko.observableArray(keywords),
isVisible: ko.dependentObservable(function(){
var visible = false;
if (viewModel.selectedKeyword() || viewModel.keywordIsDirty()) {
ko.utils.arrayForEach(keywords, function(keyword) {
if (keyword === viewModel.selectedKeyword()){
visible = true;
}
});
if (!visible) {
viewModel.users.remove(this);
}
}
return visible;
})
}
};
function Keyword(count, word){
return{
count: ko.observable(count),
word: ko.observable(word)
}
};
var viewModel = {
users: ko.observableArray([]),
keywords: ko.observableArray([]),
selectedKeyword: ko.observable(),
keywordIsDirty: ko.observable(false)
}
viewModel.selectedKeyword.subscribe(function () {
if (!viewModel.keywordIsDirty()) {
viewModel.keywordIsDirty(true);
}
});
ko.applyBindings(viewModel);
for (var i = 0; i < 500; i++) {
viewModel.users.push(
new User(i, "Man " + i, ["Beer", "Women", "Food"])
)
}
viewModel.keywords.push(new Keyword(1, "Beer"));
viewModel.keywords.push(new Keyword(2, "Women"));
viewModel.keywords.push(new Keyword(3, "Food"));
viewModel.keywords.push(new Keyword(4, "Cooking"));
And the View code:
<ul data-bind="template: { name: 'keyword-template', foreach: keywords }"></ul><br />
<ul data-bind="template: { name: 'user-template', foreach: users }"></ul>
<script id="keyword-template" type="text/html">
<li>
<label><input type="radio" value="${word}" name="keywordgroup" data-bind="checked: viewModel.selectedKeyword" /> ${ word }<label>
</li>
</script>
<script id="user-template" type="text/html">
<li>
<span data-bind="visible: isVisible">${ $data.name }</span>
</li>
</script>
Your isVisible dependentObservable has created a dependency on itself and is recursively trying to evaluate itself based on this line:
if (!visible) {
viewModel.users.remove(this);
}
So, this creates a dependency on viewModel.users, because remove has to access the observableArray's underlying array to remove the user. At the point that the array is modified, subscribers are notified and one of the subscribers will be itself.
It is generally best to not change the state of any observables in a dependentObservable. you can manually subscribe to changes to a dependentObservable and makes your changes there (provided the dependentObservable does not depend on what you are changing).
However, in this case, I would probably instead create a dependentObservable at the viewModel level called something like filteredUsers. Then, return a version of the users array that is filtered.
It might look like this:
viewModel.filteredUsers = ko.dependentObservable(function() {
var selected = viewModel.selectedKeyword();
//if nothing is selected, then return an empty array
return !selected ? [] : ko.utils.arrayFilter(this.users(), function(user) {
//otherwise, filter on keywords. Stop on first match.
return ko.utils.arrayFirst(user.keywords(), function(keyword) {
return keyword === selected;
}) != null; //doesn't have to be a boolean, but just trying to be clear in sample
});
}, viewModel);
You also should not need the dirty flag, as dependentObservables will be re-triggered when any observables that they access have changed. So, since it accesses selectedKeyword, it will get re-evaluated whenever selectedKeyword changes.
http://jsfiddle.net/rniemeyer/mD8SK/
I hope that I properly understood your scenario.