I have a simple object in a controller which can sometimes be empty ({}).
app.controller('TestController', function() {
var vm = this;
vm.testObject = {};
});
I want to hide or show some DOM-elements in the corresponding template when the object is empty or not.
I tried to do it with a simple <div ng-if="vm.testObject"> but when vm.testObject === {} it is considered true in the ng-if.
<div ng-controller="TestController as vm">
<div ng-if="vm.testObject">
Test Object is not empty
</div>
<div ng-if="!vm.testObject">
Test Object is empty
</div>
</div>
Is there a simple way to check for an empty object in the template? Preferably without adding new variables to the scope.
Here is a working Plunker:
http://plnkr.co/edit/Qed2MKmuedcktGGqUNi0?p=preview
You should use an AngularJs filter:
Javascript:
app.filter('isEmpty', [function() {
return function(object) {
return angular.equals({}, object);
}
}])
Html template:
<div ng-if="!(vm.testObject | isEmpty)">
Test Object is not empty
</div>
<div ng-if="vm.testObject | isEmpty">
Test Object is empty
</div>
Updated plunkr: http://plnkr.co/edit/J6H8VzUKnsNv1vSsRLfB?p=preview
Are you ok with moving the equality check to the ng-if?
<div ng-controller="TestController as vm">
<div ng-if="!equals({}, vm.testObject)">
Test Object is not empty
</div>
<div ng-if="equals({}, vm.testObject)">
Test Object is empty
</div>
</div>
Otherwise, provide a helper on the scope
app.controller('TestController', function() {
var vm = this;
vm.testObject = {};
vm.empty = function() {
return vm.testObject === {};
};
});
then
<div ng-controller="TestController as vm">
<div ng-if="!vm.empty()">
Test Object is not empty
</div>
<div ng-if="vm.empty()">
Test Object is empty
</div>
</div>
This is an old thread but I find easier to check if the Object has keys.
<div ng-controller="TestController as vm">
<div ng-if="Object.keys(vm.testObject).length">
Test Object is not empty
</div>
<div ng-if="!Object.keys(vm.testObject).length">
Test Object is empty
</div>
</div>
It's simple and readable.
This will work. check the Length
<div ng-if="!!vm.testObject && vm.testObject.length > 0">
Test Object is not empty
</div>
You can convert the object to a JSON string using the built-in AngularJS json filter and do a comparison like this:
<div ng-if="vm.testObject | json) != '{}'">
Test Object is not empty
</div>
Related
I have an ng-repeat that has an ng-if attached to it, with a child element that I am changing with an ng-click. The code looks something like the following:
<div ng-repeat="object in objects" ng-if="show">
<div ng-click="show = !show">Show</div>
</div>
Lets say I had 2 objects, it would load two repeated divs, and there would be two 'show' elements. When I click show, it will only remove one of the repeated elements from the page. I need it to remove both. Thoughts?
If you want to hide all I would wrap all of it in an outer div and place the "ng-if" there.
<div ng-if="show">
<div ng-repeat="object in object">
<div ng-click="show = !show">Show</div>
</div>
</div>
I would however advise to place any logic that modifies data in the TS file instead of in the html view.
Your template is almost correct, the only thing that is worth mentioning is that:
The scope created within ngIf inherits from its parent scope
using prototypal inheritance.
The main caveat of prototypal inheritance is that setting a primitive value on the child scope shadows the value on the parent scope. There are different approaches of how to avoid this, see the code snippet below:
angular.module('app', [])
.controller('mainController', function mainController($scope) {
var ctrl = this;
$scope.show = true;
$scope.showList = {value: true};
$scope.objects = [{}, {}, {}];
$scope.toggleShowVar = function(){
$scope.show = !$scope.show;
};
ctrl.show = true;
});
<!-- JS -->
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.js"></script>
<body ng-app="app">
<div class="container" ng-controller="mainController as $mainCtrl">
<p>This will not work due to scope prototypal inheritance:</p>
<div ng-if="show">
<div ng-repeat="object in objects">
<div ng-click="show = !show">Show {{show}}</div>
</div>
</div>
<p>Using "controller as" will help us:</p>
<div>
<div ng-repeat="object in objects" ng-if="$mainCtrl.show">
<div ng-click="$mainCtrl.show = !$mainCtrl.show">Show {{$mainCtrl.show}}</div>
</div>
</div>
<p>Or simply using "dot in the model":</p>
<div>
<div ng-repeat="object in objects" ng-if="showList.value">
<div ng-click="showList.value = !showList.value">Show {{showList.value}}</div>
</div>
</div>
<p>Or using controller method for toggle:</p>
<div>
<div ng-repeat="object in objects" ng-if="show">
<div ng-click="toggleShowVar()">Show {{show}}</div>
</div>
</div>
<p>Or using $parent to change it in the controller's scope:</p>
<div>
<div ng-repeat="object in objects" ng-if="$parent.show">
<div ng-click="$parent.$parent.show = !$parent.$parent.show ">Show {{$parent.show}}</div>
</div>
</div>
</div>
</body>
In my angular application, I came into a filter related issue. I have reproduced this issue with a simple live demo as bellow:
https://jsfiddle.net/baoqger/fpo3j6gx/2/
<div ng-app="app">
<div ng-controller="filterCtrl as f">
<input type="text" ng-model="f.inputdata"></input>
<span ng-click="f.setFilter('lowercase')">First Filter</span>
<span ng-click="f.setFilter('uppercase')">Second Filter</span>
<div ng-bind="f.inputdata | f.filtername"></div>
</div>
click First Filter or Second Filter will trigger the setFilter function.
function filterCtrl() {
this.setFilter = function ( name ){
this.filtername = name;
}.bind(this);
}
angular
.module('app', [])
.controller('filterCtrl', filterCtrl)
In the controller, the filtername will be set to lowercase or upper case, depends on which button was clicked as mentioned above.
Then set the filtername as filter method as below:
<div ng-bind="f.inputdata | f.filtername"></div>
But based on the error message, it seems that angular system can't support such usage. Any help?
Try this solution:
Javascript:
.controller('filterCtrl', ['$filter' function($filter){
var self = this;
self.setFilter = function(name){
self.filter = $filter(name);
}
}]);
HTML:
<div ng-app="app">
<div ng-controller="filterCtrl as f">
<input type="text" ng-model="f.inputdata"></input>
<span ng-click="f.setFilter('lowercase')">First Filter</span>
<span ng-click="f.setFilter('uppercase')">Second Filter</span>
<div ng-bind="f.filter?f.filter(f.inputdata):f.inputdata"></div>
</div>
my Angular code returns [object Object]. I am calling 2 controllers on different pages. First one sets the data on ng-click and the second one gets (displays) the data. Here is the code:
Angular App code:
var careerApp = angular.module("careerApp", []);
careerApp.factory('myService', function () {
var savedData = {};
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
});
careerApp.controller("JobList", function ($scope,myService) {
myService.set(data);
});
careerApp.controller("JobSelection", function ($scope, myService) {
$scope.jobname = myService.get();
});
HTML on Page 1
<div class="center-details" ng-controller="JobList">
<div class="details" ng-click="set(data)" >
<h2 class="name" ng-model="jobtitle">
Winter
</h2>
<p><b>Job ID#</b> <span class="jobid">2017-01</span></p>
</div>
</div>
HTML on Page 2
<div ng-controller="JobSelection">
<label ng-bind="jobname"></label>
</div>
You are bringing the whole object in
<label ng-bind="jobname"></label>
If you intented to write the object with a better formatting try changing it to:
<label> {{ jobname | json }}</label>
This way it will be formatted and printed as a json object.
Use Angular expressions intead of ng-bind. Otherwise you will have to specify a specific property of your object.
page 1
<div class="center-details" ng-controller="JobList">
<div class="details" ng-click="set('Winter')" >
<h2 class="name">
Winter
</h2>
<p><b>Job ID#</b> <span class="jobid">2017-01</span></p>
</div>
</div>
Controller:
careerApp.controller("JobList", function ($scope,myService) {
$scope.set= function(data){
myService.set(data);
}
});
page 2
<label ng-bind="jobname"></label>
<label>{{jobname}}</label>
How to make it dynamically, based on the input
<input stype="text" ng-model="jobTitle" ng-change="set()" >
<h2 class="name">
{{jobTitle}}
</h2>
Controller:
careerApp.controller("JobList", function ($scope,myService) {
$scope.jobTitle = "";
//This function will be called every time that jobTitle change its value.
$scope.set= function(){
myService.set($scope.jobTitle);
}
});
Notes:
Take into account that ng-model directive binds an input, select, textarea value to a property on the scope.
Since you have this assignment in your controller definition
$scope.jobname = myService.get();
If you run this controller before the user make a click it will be empty. it wont be refreshed in every click.
Aim :- To display only Single Object data.
Do I have to use ng-repeat to get the object ?
I'm relatively new to angular. Is there a better way to do this?
Html View :-
<div ng-controller="dashboardController">
<div ng-repeat="person in persons">
<span class="name">{{person.name}}</span>
<span class="title">{{person.title}}</span>
</div>
</div>
Controller Code :-
.controller('dashboardController', ['$scope', function(scope){
scope.persons = [
{
name:"George Harrison",
title:"Goof Off extraordinaire"
}
];
}])
UPDATE FOR MY FELLOW NOOBS, array vs single data set:
scope.persons = [ <-- that creates an array. Cant believe I forgot that.
scope.persons = { <-- that creates a single data set
scope.persons is an array so you have to use ng-repeat.
if you your data is an object, you don't need to use ng-repeat.
ex:
your controller
.controller('dashboardController', ['$scope', function(scope){
scope.person ={
name:"George Harrison",
title:"Goof Off extraordinaire"
}
}]);
so your html:
<div ng-controller="dashboardController">
<div>
<span class="name">{{person.name}}</span>
<span class="title">{{person.title}}</span>
</div>
This can serve your cause with current Json Structure :
<div ng-controller="dashboardController">
<div>
<span class="name"> {{persons[0].name}} </span>
<span class="title"> {{persons[0].title}} </span>
</div>
</div>
Normally if you were only getting one item , it would be an object
$scope.beatle = {
name:"George Harrison",
title:"Goof Off extraordinaire"
}
then reference that directly in view
{{beatle.name}}
Here I have a div in the ng-repeat
<div ng-class="{div0f:result.subscribed==omega.harish,div0g:!result.subscribed==omega.harish}" id="mecota0f"
ng-click="omega.harish=!result.subscribed" >
Now here result.subscribed is a boolean value coming from the services and omega.harish is my simple boolean variable
the class of this div will be according to the result.subscribed value
Now I also want to change the active class of the one of the div created in the ng-repeat but the class of other created divs are kept getting affected.
var app = angular.module('subscribeWho', ['ngResource']);
app.controller('mysubscribeWho', ['subscriberFactory',
function(subscriberFactory) {
var self = this;
subscriberFactory.get({}, function(subscriberFactory) {
self.subscriberdata = subscriberFactory.user;
});
self.harish = false;
}
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div id="border-forpb" class="col-sm-12 panel panel-default" ng-app="subscribeWho">
<div class="panel-body" id="artle_panel_body" ng-controller="mysubscribeWho as omega">
<div ng-repeat="result in omega.subscriberdata">
<div ng-class="{div0f:result.subscribed==omega.harish,div0g:!result.subscribed==omega.harish}" id="mecota0f" ng-click="omega.harish=!result.subscribed">
</div>
</div>
</div>
You are changing outer scope omega.harish value which is common for all items in the ngRepeat. Instead create local scope copy of omega.harish with ngInit directive:
<div ng-repeat="result in omega.subscriberdata">
<div ng-class="{div0f:result.subscribed==harish,div0g:!result.subscribed==harish}" id="mecota0f"
ng-init="harish = omega.harish"
ng-click="harish=!result.subscribed">
</div>
</div>