<div ng-repeat="book in books">
<h3 class="title">{{book.title}}</h3>
</div>
I use above code to get and it work fine, but I also have a search function, which will also set $scope.books. Unfortuunetly I have no control over the api, the search callback returned different scheme, means to get the title of the books I have to navigate to somewhere else, like book.category.item.title.
so I'm thinking of doing something like this
<a href="#/{{book.title || book.category.item.title}}">
check if book.title isset, else display another expression. Is it correct?
Instead I would prefer using ng-href
<a ng-href="{{getTitle()}}">link</a>
In your controller, return the url by checking logic there.
angular.module("app",[])
.controller("MainCtrl", function($scope) {
$scope.book = {};
$scope.book.title = null;
$scope.book.category = {
item : {
title : "Rafi"
}
}
$scope.getTitle = function() {
return '#/'+ ($scope.book.title || $scope.book.category && $scope.book.category.item && $scope.book.category.item.title);
}
});
DEMO
if you don't want to use controller for some reason:
<a ng-href="{{'#/'+(book.title || book.category.item.title)}}">
Related
I have an ng-repeat list with multiple filters, but as I need the elements to be hidden and not removed from the DOM, I use ng-show for the filtering.
This is how my first filter look like:
<a href="" ng-click="myFilter = ''; toggle = toggle=0; togglesec=0; mySecondFilter = {}" ng-class="{'active' : toggle==0}" >
Tot
</a>
<a href="" ng-click="myFilter = 'pa'; toggle = toggle=1; togglesec=0; mySecondFilter = {}" ng-class="{'active' : toggle==1}">
Pa
</a>
<a href="" ng-click="myFilter = 'Reposteria'; toggle = toggle=2; togglesec=0; mySecondFilter = {}" ng-class="{'active' : toggle==2}">
Reposteria
</a>
And the second one:
<div ng-init="togglesec=0; mySecondFilter = {}">
<a ng-click="mySecondFilter = {}; togglesec = togglesec=0" ng-class="{'active' : togglesec==0}">
All
</a>
<a ng-click="mySecondFilter = {'vegan': true}; togglesec = togglesec=1" ng-class="{'active' : togglesec==1}">
Vegan
</a>
<a ng-click="mySecondFilter = {'gluten': true}; togglesec = togglesec=2" ng-class="{'active' : togglesec==2}">
Gluten Free
</a>
</div>
Now, I was able to filter the ng-repeat using ng-show and the first filter like so:
<div ng-repeat="pa in products" ng-show="pa.tipus.indexOf(myFilter) != -1">
Basically it compares the value of myFilter with the pa.tipus object property, and it works OK.
But it won't work with the second filter, because mySecondFilter is an object, not a string (it needs to filter the results containing vegan:true or gluten:true)
Here's an example of my object type:
pa {
tipus : 'pa',
gluten : false,
vegan : true
}
Any tips on how to combine both filters in the same ng-show?
EDIT
I've applied the answer by Naren, but I get the following error on click on any filter:
angular.min.js:122 TypeError: Cannot create property 'vegan' on string ''.
I've also tried to initialize myFilter by adding this, but no luck, same error appears:
$scope.myFilter = {
tipus : '',
vegan : '',
gluten : '',
lactosa : ''
};
Update:
Since the user wanted a generic version for the filtering. The following function should be the answer.
$scope.validate = function(row) {
for (var key in $scope.myFilter) {
if ($filter('filter')([row], {
[key]: $scope.myFilter[key]
}).length === 0) {
return false;
}
}
return true;
}
Where we loop through an object where all the filters are stored, then return false if any of the filters are not satisfied.
Please check the below example to get a understanding of how the filter works.
JSFiddle Demo
Old:
Here is my version of the fix. Angular's $filter will be great for identifying the object with the property, but this is not possible to have written in the HTML, hence I call a function which will return true or false to the ng-show.
$scope.validate = function(row) {
if (row.tipus.indexOf($scope.myFilter) != -1) {
if ($filter('filter')([row], $scope.mySecondFilter).length !== 0) {
return true;
} else {
return false;
}
} else {
return false;
}
}
Let me explain the function. First we receive the row of the ng-repeat through the parameter of validate, after which I apply the first if condition which is basically whatever you had written earlier, this code works great btw. Then I check the row using the $filter syntax for the object present in $scope.mySecondFilter, Refer here for more on filtering with $filter, this will basically check if the row passed contains the required object property and return true or false.
Here is a working example of your code for reference.
JSFiddle Demo
Please let me know if this fixes your issue completely :)
I just followed this
JSFiddle example to create a little search box from an array object in my javascript. Now after some tweaking and research on search.object and filter:search:strict. Now that I have it filtering correctly, I modified a checkbox in the template that is checked upon loading the document and switched on or off based on a custom data attribute in the html that is updated by the json array.
How do I run this check once someone clears the search and the old items show back up again?
In the HTML I have this template
<div ng-repeat="o in objects | filter:search:strict | orderBy:'obj_name'" class="objectContainer">
<h3>{{o.obj_name}}</h3>
<label class="userToggleSwitch" >
<input class="userToggleInput" data-isactive="{{o.obj_isactive}}" type="checkbox">
<div class="slider round"></div>
</label>
</div>
In the JS
angular.element(document).ready(function(){
angular.module('searchApp',[])
.controller('searchCtrl', ['$scope','objHolder',function ($scope,objHolder) {
$scope.search = '';
$scope.objects = [];
$scope.objects = objHolder.getobjects();
}])
// fake service, substitute with your server call ($http)
.factory('objHolder',function(){
var objects = objList;
return {
getobjects : function(){
return objects;
}
};
});
});
The JS that modifies the items on document load is using jquery like this
$(document).ready(function(){
//console.log($('.userToggleInput'));
$.each($('.userToggleInput'), function(){
if($(this).data("isactive") == 1){$(this).attr('checked', true);}
if($(this).data("isactive") == 0){$(this).attr('checked', false);}
})
$('.userToggleInput').click(function(){
if ($(this).attr('checked') == undefined) {
// THIS IS WHERE WE WILL MAKE AN AJAX CALL TO A PHP CLASS
// TO REQUEST IF THE USER CAN BE TURNED ON - DUE TO LIC RESTRICTIONS
$(this).attr('checked',true);
} else {
$(this).attr('checked',false);
}
//console.log($(this).attr('checked'));
});
});
Created JS Fiddle to assist in Helping in this manner
I'm having a problem where despite a list being updated, ngRepeat does not display the information.
In the code below, the $scope.selectedImages array is being added to when images are selected using the file input. It is set up so that only unique files will be added to the list of selectedImages (no duplicates).
At the end of the function, scope.selectedImages prints the array with the new values, but in the view, ngRepeat does not add the new list items with the image names.
Other questions regarding this issue said that $scope.$apply should fix the problem, but for me it is not. Am I using it wrong or is there something else that I am misunderstanding.
EDIT: View 1 and View 2 are in separate HTML pages that both rely on the same controller.
View 1:
<input type="file" multiple accept="image/*" class="file-input" id="img-upload-btn" onchange="angular.element(this).scope().select(this)">
<md-button class="md-primary md-raised sidenav-btn" ng-click="proxy('img-upload-btn')">
Select <strong>Images</strong>
</md-button>
The proxy function allows the md-button to click the input.
View 2:
<div layout-padding flex ng-controller="upload-controller">
<ul>
<li ng-repeat="im in selectedImages">{{im.name}}</li>
</ul>
</div>
Controller:
$scope.selectedImages = [];
$scope.select = function(element) {
$scope.$apply(function(scope) {
var justFiles = $.map(element.files, function(val, key) {
return val;
}, true);
var fileEquality = function(f1, f2) {
if (f1.name != f2.name) return false;
if (f1.size != f2.size) return false;
if (f1.type != f2.type) return false;
if (f1.lastModified != f2.lastModified) return false;
return true;
}
// inefficient, find better way later
for (i in justFiles) {
var contains = false;
var file = justFiles[i];
for (i in scope.selectedImages) {
if (fileEquality(file, scope.selectedImages[i])) {
contains = true;
break;
}
}
if (!contains) scope.selectedImages.push(file);
}
console.log(scope.selectedImages);
});
};
Your input field template and controller template have to different scopes. Use $rootScope.selectedImages in your case.
I am using a controller to load an array and then showing and hiding content based on whether the array is loaded or not. However, the value of the array evaluates differently for different instances of ng-show and ng-class.
My service (where the array is updated) is:
'use strict';
angular.module('yeahdoneit')
.factory('Adate', function ($resource, $http, Alerts, Styling, $filter) {
return {
Adates: null,
getPersonAdates: function(personid, callback) {
var self = this;
var promise = $http.get('/api/personadates/'+ personid);
promise.then(function(response){
self.Adates = response.data;
callback();
Styling.setLoading(false);
}).catch(function(e){
throw e;
}).then(function(res){
// do more stuff
}).catch(function(e){
// handle errors in processing or in error.
Alerts.setMessage(
Alerts.getAlertTypes().alertType_Error,
Alerts.getAlertTitles().alertTitle_Error,
Alerts.getAlertMessages().alertMessage_ErrorGettingAdate,
false);
callback();
Styling.setLoading(false);
});
}
}
});
This is the controller that calls the service to load the array:
angular.module('ydi')
.controller('MypeopleCtrl', function ($scope, $http, Styling, Person, Adate, Alerts, $timeout, $filter) {
$scope.Styling = Styling;
$scope.activeItem = null;
$scope.Person = Person;
$scope.Adate = Adate;
Person.getMyPersons();
$scope.loadPerson = function($event, personid) {
Styling.setLoading(true);
$scope.activeItem = personid;
Adate.getPersonAdates(personid, function() {
console.log("ADATE - Adate.Adates", Adate.Adates);
if (Person.ThePerson === null || personid !== Person.ThePerson._id)
{
Person.ThePerson = $filter('filter')($scope.Person.Persons, {_id:personid})[0];
}
});
console.log("MYPEEPS: Adate.Adates",Adate.Adates);
};
});
This is the template view that displays the code:
<div id="person" ng-class="{ active: Adate.Adates }" >
<div ng-show="Adate.Adates">
... content here ...
</div>
<div ng-show="!Adate.Adates" class="rc_instruction">
<p class="sidenotes"><i class="fa fa-hand-o-left"></i> Click on one of your people over there</p>
<p class="sidenotes"><i class="fa fa-hand-o-right"></i> Their details will appear over here</p>
</div>
</div>
When the view is displayed and the getPersonAdates runs and populates the array, if the array has content, it all works fine, but when that array is empty the following happens:
The ng-class on the parent div evaluates to true and sets the class to active.
The ng-show on the first child div evaluates to false and does not display. (it has an ng-hide attribute).
The ng-show on the second child div also evalutes to false and does not display! (it has an ng-hide attribute).
This is the output for those two child div tags in the element inspector in chrome, when the array is empty:
<div ng-show="Adate.Adates" class="ng-hide"> ... </div>
<div ng-show="!Adate.Adates" class="rc_instruction ng-hide"> ... </div>
Is there any reason why they would evaluate differently? Am I missing something obvious?
In first case ng-class evaluate you expression as true because in JavaScript Array is an instance of Object. It checks if your Adate.Adates exists and returns true
Just try:
if ( Adate.Adates ) {
// this code will execute when your Adates has an empty array
}
You can fix it using .length property:
<div id="person" ng-class="{ active: Adate.Adates.lenght }" >
However ng-show directive uses toBoolean function to evaluate expression:
function toBoolean(value) {
if (value && value.length !== 0) {
var v = lowercase("" + value);
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
} else {
value = false;
}
return value;
}
Thats why you have a different results with ng-show and ng-class
Update:
This problem fixed in version 1.3, where ng-show should not use this function anymore:
ngShowHide.js v.1.2.27
ngShowHide.js v.1.3.x
Github issue: https://github.com/angular/angular.js/issues/3969
I'm new to angular and experiencing some difficulty implementing a 'live search' type of feature. I have my JSON set as a variable in a javascript file and I am able to display that in the html with no problem. I then have a 'list' radio box which changes the size of the display. This is functional as well. I run into trouble, however, when accessing the reverse filter option which is denoted with another radio button. The intended result is to filter the book titles in reverse by their name, ye nothing happens. Here is my JSON if you want to see the structure. And below is the code where I try to perform the filter reverse action:
<div class="container result">
<div ng-class="list ? 'col-md-6' : 'col-md-4'" class="books" ng-repeat="books in books | filter:search | orderBy:'books.doc.name':reverse"> <a href="https://address/{{books.doc.name}}" target="_blank">
<img ng-class="list ? 'col-md-2' : 'col-md-12'" ng-src="{{books.doc.thumbnail_590_url}}" alt="Click to read {{books.doc.name}}" title="Click to read {{books.doc.name}}" class="img-thumbnail" /></a>
<h4 ng-class="list ? 'col-md-10' : 'col-md-12'">{{books.doc.name}}</h4>
</div>
and heres the js:
angular.module("myApp",["ngSanitize"])
.filter('replace', function () {
var pat = / /gi;
return function (text) {
var clean = text.replace(pat, "-");
var temp = clean.split("---");
if (temp.length>1) {
clean = temp[0];
}
return clean;
};
})
.controller("Example", function($scope, $interval) {
$scope.search = "orig";
$scope.books = books;
$scope.reverse = false;
$scope.list = false;
});
I figured this out. Angular filter will not work on a json object, rather the data must be in the form of a json array. I removed the highest level enclosing curly brackets and replaced them with square brackets in addition to removing the numbers and colons like "1272:" that can be seen in the json file I posted. Taking these steps converted my data to a json array and allowed me to use the live search functionality.