theres my progress bar code:
<div class="col-sm-4" ng-repeat="var in payloadNbrMissionParStatut">
<h4 class="no-margin">{{var.number}}</h4>
<progressbar value={{tom}} class="progress-xs no-radius no-margin" type={{gg}} ng-if="var.des=='Clos' ? gg='danger' : var.des=='En cours'? gg='warning' :gg='info' " ></progressbar>
{{var.des}}
</div>
the problem is in the value: when i populate it with static data it works good, but when i populate wit data come from the controller it doesnt work.
var is a reserved JS keyword. As errors inside directive expressions are silent, you aren't seeing any alerts because it's simply being ignored. Change var to something else and it should work.
Related
I have a option in the back-end to let user change icon in default and in hover state. This is the code
ng-class="{'{{getAnswerButtonHoverIcon()}}':hovering, '{{getAnswerButtonIcon()}}' : !hovering}
But the problem is, when the user selected the same icon for default state and hover state, like for example they have selected "fa-circle-o" the output of the HTML will be
ng-class="{'fa-circle-o':hovering, 'fa-circle-o' : !hovering}"
and it doesn't work anymore. When I try to test it out, it removes the "fa-cricle-o" class on hover. Any idea why is it happening? And if you can suggest better solution, it will be greatly appreciated. Thanks!
I mentioned in a comment that I would probably have a single function but I think I misunderstood your question at first. Building on Joshua's approach here is something that could work for you: if the two functions return the same value then it still works.
I feel that this is not the nicest solution since it is generating a string into a class, but the same can be changed to ng-class and {hovering ? sg() : sgelse()} as value. I hope it shows you a way to handle these kind of interactions.
Let me know if it does/doesn't work so that we could further help you.
var app = angular.module("mainModule",[]);
app.controller("mainCtrl", function($scope){
$scope.icon = {};
$scope.icon.hovering = false;
$scope.getAnswerButtonHoverIcon = function(){
return 'bluebg';
};
$scope.getAnswerButtonIcon = function(){
return 'redbg';
};
});
.bluebg{background-color:blue}
.redbg{background-color:red}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="mainModule">
<div ng-controller="mainCtrl">
<div class="{{ icon.hovering ? getAnswerButtonHoverIcon() : getAnswerButtonIcon() }}"
ng-mouseenter="icon.hovering = true" ng-mouseleave="icon.hovering = false">
<p>I have different backgrounds</p>
</div>
</div>
</div>
You can also use the ng-class like this, which will just apply the class the function outputs:
ng-class="(hovering ? '{{getAnswerButtonHoverIcon()}}': '{{getAnswerButtonIcon()}}')
I'm trying to make a minimal but fancy AngularJS tutorial example, and I am running into an issue where after updating the entire tree for a model (inside the scope of an ng-change update), a template that is driven by a top-level ng-repeat is not re-rendered at all.
However, if I add the code $scope.data = {} at a strategic place, it starts working; but then the display flashes instead of being nice and smooth. And it's not a great example of how AngularJS automatic data binding works.
What am I missing; and what would be the right fix?
Exact code - select a country from the dropdown -
This jsFiddle does not work: http://jsfiddle.net/f9zxt36g/
This jsFiddle works but flickers: http://jsfiddle.net/y090my10/
var app = angular.module('factbook', []);
app.controller('loadfact', function($scope, $http) {
$scope.country = 'europe/uk';
$scope.safe = function safe(name) { // Makes a safe CSS class name
return name.replace(/[_\W]+/g, '_').toLowerCase();
};
$scope.trunc = function trunc(text) { // Truncates text to 500 chars
return (text.length < 500) ? text : text.substr(0, 500) + "...";
};
$scope.update = function() { // Handles country selection
// $scope.data = {}; // uncomment to force rednering; an angular bug?
$http.get('https://rawgit.com/opendatajson/factbook.json/master/' +
$scope.country + '.json').then(function(response) {
$scope.data = response.data;
});
};
$scope.countries = [
{id: 'europe/uk', name: 'UK'},
{id: 'africa/eg', name: 'Egypt'},
{id: 'east-n-southeast-asia/ch', name: 'China'}
];
$scope.update();
});
The template is driven by ng-repeat:
<div ng-app="factbook" ng-controller="loadfact">
<select ng-model="country" ng-change="update()"
ng-options="item.id as item.name for item in countries">
</select>
<div ng-repeat="(heading, section) in data"
ng-init="depth = 1"
ng-include="'recurse.template'"></div>
<!-- A template for nested sections with heading and body parts -->
<script type="text/ng-template" id="recurse.template">
<div ng-if="section.text"
class="level{{depth}} section fact ng-class:safe(heading);">
<div class="level{{depth}} heading factname">{{heading}}</div>
<div class="level{{depth}} body factvalue">{{trunc(section.text)}}</div>
</div>
<div ng-if="!section.text"
class="level{{depth}} section ng-class:safe(heading);">
<div class="level{{depth}} heading">{{heading}}</div>
<div ng-repeat="(heading, body) in section"
ng-init="depth = depth+1; section = body;"
ng-include="'recurse.template'"
class="level{{depth-1}} body"></div>
</div>
</script>
</div>
What am I missing?
You changed reference of section property by executing section = body; inside of ng-if directives $scope. What happened in details (https://docs.angularjs.org/api/ng/directive/ngIf):
ng-repeat on data created $scope for ng-repeat with properties heading and section;
Template from ng-include $compile'd with $scope from 1st step;
According to documentation ng-if created own $scope using inheritance and duplicated heading and section;
ng-repeat inside of template executed section = body and changed reference to which will point section property inside ngIf.$scope;
As section is inherited property, you directed are displaying section property from another $scope, different from initial $scope of parent of ngIf.
This is easily traced - just add:
...
<script type="text/ng-template" id="recurse.template">
{{section.Background.text}}
...
and you will notice that section.Background.text actually appoints to proper value and changed accordingly while section.text under ngIf.$scope is not changed ever.
Whatever you update $scope.data reference, ng-if does not cares as it's own section still referencing to previous object that was not cleared by garbage collector.
Reccomdendation:
Do not use recursion in templates. Serialize your response and create flat object that will be displayed without need of recursion. As your template desired to display static titles and dynamic texts. That's why you have lagging rendering - you did not used one-way-binding for such static things like section titles. Some performance tips.
P.S. Just do recursion not in template but at business logic place when you manage your data. ECMAScript is very sensitive to references and best practice is to keep templates simple - no assignments, no mutating, no business logic in templates. Also Angular goes wild with $watcher's when you updating every of your section so many times without end.
Thanks to Apperion and anoop for their analysis. I have narrowed down the problem, and the upshot is that there seems to be a buggy interaction between ng-repeat and ng-init which prevents updates from being applied when a repeated variable is copied in ng-init. Here is a minimized example that shows the problem without using any recursion or includes or shadowing. https://jsfiddle.net/7sqk02m6/
<div ng-app="app" ng-controller="c">
<select ng-model="choice" ng-change="update()">
<option value="">Choose X or Y</option>
<option value="X">X</option>
<option value="Y">Y</option>
</select>
<div ng-repeat="(key, val) in data" ng-init="copy = val">
<span>{{key}}:</span> <span>val is {{val}}</span> <span>copy is {{copy}}</span>
</div>
</div>
The controller code just switches the data between "X" and "Y" and empty versions:
var app = angular.module('app', []);
app.controller('c', function($scope) {
$scope.choice = '';
$scope.update = function() {
$scope.data = {
X: { first: 'X1', second: 'X2' },
Y: { first: 'Y1', second: 'Y2' },
"": {}
}[$scope.choice];
};
$scope.update();
});
Notice that {{copy}} and {{val}} should behave the same inside the loop, because copy is just a copy of val. They are just strings like 'X1'. And indeed, the first time you select 'X', it works great - the copies are made, they follow the looping variable and change values through the loop. The val and the copy are the same.
first: val is X1 copy is X1
second: val is X2 copy is X2
But when you update to the 'Y' version of the data, the {{val}} variables update to the Y version but the {{copy}} values do not update: they stay as X versions.
first: val is Y1 copy is X1
second: val is Y2 copy is X2
Similarly, if you clear everything and start with 'Y', then update to 'X', the copies get stuck as the Y versions.
The upshot is: ng-init seems to fail to set up watchers correctly somehow when looped variables are copied in this situation. I could not follow Angular internals well enough to understand where the bug is. But avoiding ng-init solves the problem. A version of the original example that works well with no flicker is here: http://jsfiddle.net/cjtuyw5q/
If you want to control what keys are being tracked by ng-repeat you can use a trackby statement: https://docs.angularjs.org/api/ng/directive/ngRepeat
<div ng-repeat="model in collection track by model.id">
{{model.name}}
</div>
modifying other properties won't fire the refresh, which can be very positive for performance, or painful if you do a search/filter across all the properties of an object.
All:
I wonder how can I set a OR operator on a HTML string in Angualar Template, something like:
<div>{{value || <h6>No Header for now.</h6>}}</div>
The logic is if value is a string but not undefined, we show the value text, otherwise we show a error "No Header for Now" wrapped up by <h6>.
I do not know why this expression can not be correctly interpreted?
Thanks
This can be solved with ng-if:
<div ng-if="value">value</div>
<div ng-if="!value"><h6>No Header for now.</h6></div>
You can add specific attributes (e.g. class) and/or directives (e.g. ng-click) on each <div>.
The problem with using a single element is that you have to repeat your condition several times:
<div ng-class="{ value: 'class1', !value: 'class2' }"
ng-click="value ? action1() : action2()"
ng-bind-html="value || html">
</div>
You have to put a string, not an expression: <h6>No Header for now.</h6> is an invalid js expression. '<h6>No Header for now.</h6>' is a string and can be displayed in the {{ }}.
<div>{{value || '<h6>No Header for now.</h6>' }}</div>
or
<div>{{value != null ? value : '<h6>No Header for now.</h6>' }}</div>
I'm convinced the 2nd works.
EDIT:
If you want to add html code in the {{ }}, it is another problem. See AngularJS : Insert HTML into view, the filter 'sanitize' in 2nd answer should help you ( call {{ '<h1>test</h1>' | sanitize }} and it should work.
EDIT 2:
In a js file:
angular.module('yourapp')
.filter("sanitize", ['$sce', function($sce) {
return function(htmlCode){
return $sce.trustAsHtml(htmlCode);
}
}]);
In view:
<div>{{value || '<h6>No Header for now.</h6>' | sanitize }}</div>
In dont know if you need 'ngResource' to use $sce, if it doesn't work, you will have to install angular-resource :/
So wanted to have a spinning cube with pictures on, because that is what all the cool kids want. Then decided that I wanted to have it all in a database and use the mean stack, so set that up.
Here comes the problem. On the index.html version, the cube sometimes messes up and moves to the screen blocking everything else out. Just open the link below and click the menu a few different menu options a few time and you'll see what I mean.
http://v-ghost.port0.org:8081/TESTCube/public/index.html
Worked a long while trying to figure out what was wrong, and it turns out that it is related to how I load the menu, I tried to generate it using ng-bind-html.
<figure class="front"><div id="front-page" ng-bind-html="showpane('front')"></div></figure>
<figure class="back"><div id="front-page" ng-bind-html="showpane('back')"></div></figure>
<figure class="right"><div id="front-page" ng-bind-html="showpane('right')"></figure>
<figure class="left"><div id="front-page" ng-bind-html="showpane('left')"></figure>
<figure class="top"><div id="front-page" ng-bind-html="showpane('top')"></figure>
<figure class="bottom"><div id="front-page" ng-bind-html="showpane('bottom')"></figure>
Strangely, if I change this to just use text (i.e. less fancy way):
<button class="show-front">Front</button><br/>
<button class="show-back">Back</button><br/>
<button class="show-right">Right</button><br/>
<button class="show-left">Left</button><br/>
<button class="show-top">Top</button><br/>
<button class="show-bottom">Bottom</button><br/>
Try it on
http://v-ghost.port0.org:8081/TESTCube/public/index-nobind.html, unbreakable (famous last words).
What I can't understand is why, why does me calling a function in a button break the cubes path?
Also, is there an easier way to get the value of a string in an array then building a function to get it in a ng-bind?
Tried to make it as easy as possible to test around, should be just to copy the html if you want to give it a try.
After much fiddling about I never manage to figure out why ng-bind behaved like this. And without a error message it was pretty hard to identify the problem.
If you have the same problem, one way to get around it is to just change the controller to present the data under an object. So start by defining the controller as app
<body ng-app="DBFScube" ng-controller="AppCtrl as app">
Then call a sub function (getmenuname) for each option.
<button class="show-front">{{app.getmenuname('front')}}</button><br/>
<button class="show-back">{{app.getmenuname('back')}}</button><br/>
<button class="show-right">{{app.getmenuname('right')}}</button><br/>
<button class="show-left">{{app.getmenuname('left')}}</button><br/>
<button class="show-top">{{app.getmenuname('top')}}</button><br/>
<button class="show-bottom">{{app.getmenuname('bottom')}}</button>
The controller looks like this
var app = this;
//
//$http.get("http://localhost:3000").success(function(CubeSides){
// app.CubeSides = CubeSides;
//})
$http.get("http://localhost:3000").success(function(CubeSides){
$scope.sides=CubeSides;
console.log("Loading data");
})
app.getmenuname = function(side) {
if($scope.sides === undefined) {
console.log("Not loaded fside " + side );
} else {
console.log("Got menu pane " + $scope.sides);
var fside = $filter('filter')($scope.sides, function (d) {return d.side === side;})[0];
console.log("Sending: " + fside.menuname);
return fside.menuname;
}
}
This probably isn't the right way of doing it, but if all you need to do is set the name on some buttons, it does the trick:)
Hi I am a newbie to angular js and I am hoping someone can help me out with the following problem.
I have a numeric field called numAdults and I need to show a set of field (such as name, address, telephone etc ) numAdult times to get those information for each of those person.
Here is the jsfiddle for the problem jsfiddle link
Here is also an overview of code of the controller
function bookingController($scope){
$scope.numAdults = 1;
$scope.personLoop = function(){
console.log('personLoop called')
return new Array($scope.numAdults);
//return new Array(2);
}
the html
<label for="book_num_adults">Number of adults:</label>
<input id="book_num_adults" type="text" ng-model="numAdults">
<div class="row" ng-repeat="t in personLoop()" style="border:2px solid red;margin-top:10px">
<h4>Person {{$index+1}}</h4>
<input placeholder="name"><br>
<input placeholder="address"><br>
<input placeholder="telephone"><br>
</div>
Can you also help me with how to transform this as an module ( not just a controller based )
Thank you in advance!
Your Fiddle was riddled with errors...
http://jsfiddle.net/bRgTR/5/
Under Frameworks & Extensions, you need to change the 2nd dropdown from "onLoad" to one of the "No wrap" options
Your controller definition is mangled. It's supposed to be: .controller('name', ['depname', function (depname) { })]); -- you had your closing array misplaced.
You should really use semi-colons.
You don't create an array of 5 items in JavaScript like this: var a = new Array(5), that creates an array that contains a 5. Instead, you should do var a = []; a.length = 5;