Observer not getting fired in Ember - javascript

I have a very weird issue, I have two observers in my App but only one of them fires properly. I am not sure why this is happening.
Here's the controller in question:
App.TwodController = Ember.ArrayController.extend({
//filteredContent : null,
init : function() {
this.set('filteredContent', []);
},
//sortProperties : ['firstname'],
//sortAscending : true,
selectedExperience : null,
filterExperience : function() {
var exp = this.get('selectedExperience.exp');
var filtered = this.get('arrangedContent').filterProperty('experience', exp);
this.set("filteredContent", filtered);
}.observes('selectedExperience'),
experience : [{
exp : "1"
}, {
exp : "2"
}, {
exp : "3"
}, {
exp : "4"
}, {
exp : "5"
}],
selectedDesignation : null,
filterDesignation : function() {
var designation = this.get('selectedDesignation.designation');
var filtered = this.get('arrangedContent').filterProperty('designation', designation);
this.set("filteredContent", filtered);
}.observes('selectedDesignation'),
designations : [{
designation : "Design",
id : 1
}, {
designation : "Writer",
id : 2
}, {
designation : "Script",
id : 3
}, {
designation : "Storyboard",
id : 4
}, {
designation : "Workbook",
id : 5
}],
actions : {
filterExperience : function() {
var experience = this.get('selectedExperience.exp');
var filtered = this.get('content').filterProperty('experience', experience);
this.set("filteredContent", filtered);
},
refresh : function() {
var refresh = this.get('content');
this.set("filteredContent", refresh);
}
},
filteredContent : function() {
var searchText = this.get('searchText'), regex = new RegExp(searchText, 'i');
return this.get('model').filter(function(item) {
var fullname = item.firstname + item.lastname;
return regex.test(fullname);
});
}.property('searchText', 'model')
});
As you can see, I have filterDesignation & filterExperience. But only filterExperience works as expected not the filterDesignation.
Moreover here is the HTML Template for that Controller:
<script type="text/x-handlebars" id="twod">
<div class="row">
<div class="span4">
<img src="/img/2DPipeline.jpg"/>
</div>
<div class="span4">
<h4>2D Roles</h4>
{{view Ember.Select
contentBinding="designations"
optionValuePath="content.id"
optionLabelPath="content.designation"
selectionBinding="selectedDesignation"
prompt="Please Select a Role"}}
{{view Ember.Select
contentBinding="experience"
optionValuePath="content.exp"
optionLabelPath="content.exp"
selectionBinding="selectedExperience"
prompt="Please Select Experience"}}
<br/>
<!-- <button {{action 'filter'}}>Filter By Designation</button>
<button {{action 'filterExperience'}}>Filter By Experience</button>
<button {{action 'refresh'}}>Refresh</button> --> </div>
<div class="span3">
<h4>People with Roles</h4>
{{input type="text" value=searchText placeholder="Search"}}
<div class="row">
<div class="span2">
<ul>
{{#each item in filteredContent}}
<li>{{#link-to 'twoduser' item}}{{item.firstname}} {{item.lastname}} {{/link-to}}</li>
{{/each}}
</ul>
</div>
<div class="row">
<div class="span3">
{{outlet}}
</div>
</div>
</div>
</div>
</div>
</script>
Here's the full JSBin as well.
What might be the issue?
Edit: The Search box in "Twod" Template doesn't work. Any ideas why?

The observer is working. Your JSON appears to contain additional whitespace. The designation value for each record has a space on the front and back of the value.
designation: " Design "
the filter is looking for
designation: "Design"
trimming your designation on the server should fix this up.
Additional issue(s):
Is filteredContent an array, or a computed property? You are blasting away your computed property on init and replacing the filteredContent computed property with an empty array
init : function() {
this.set('filteredContent', []);
},
or this?
filteredContent : function() {
var searchText = this.get('searchText'), regex = new RegExp(searchText, 'i');
return this.get('model').filter(function(item) {
var fullname = item.firstname + item.lastname;
return regex.test(fullname);
});
}.property('searchText', 'model')
Additionally you are filtering in multiple places, so one filter will totally blast away another filter. So I ripped out the observes for each one of those drop downs, and removed the init destroying the filteredContent array.
filteredContent : function() {
var designation = this.get('selectedDesignation.designation'),
hasDesignation = !Ember.isEmpty(designation),
experience = this.get('selectedExperience.exp'),
hasExperience = !Ember.isEmpty(experience),
searchText = this.get('searchText'),
hasSearch = !Ember.isEmpty(searchText),
regex = hasSearch ? new RegExp(searchText, 'i') : undefined;
return this.get('model').filter(function(item) {
var fullname = Em.get(item,'firstname') + Em.get(item,'lastname');
return (!hasDesignation || Em.get(item, 'designation') === designation) &&
(!hasExperience || Em.get(item, 'experience') === experience) &&
(!hasSearch || regex.test(fullname));
});
}.property('searchText', 'model', 'selectedExperience.exp', 'selectedDesignation.designation')
http://jsbin.com/aHiVIwU/27/edit
BTW
If you don't want it to show anything until they've filtered at least one thing, instead of setting filtered content to an empty array in the init, you would do that in the computed property, filteredContent, like so (before the filter):
if(!(hasDesignation|| hasExperience || hasSearch)) return [];
Ember isEmpty
Verifies that a value is `null` or an empty string, empty array,
or empty function.
Constrains the rules on `Ember.isNone` by returning false for empty
string and empty arrays.
```javascript
Ember.isEmpty(); // true
Ember.isEmpty(null); // true
Ember.isEmpty(undefined); // true
Ember.isEmpty(''); // true
Ember.isEmpty([]); // true
Ember.isEmpty('Adam Hawkins'); // false
Ember.isEmpty([0,1,2]); // false
```

Related

ngFilter equals with 2 var in angular

I have a little problem, I would like to do a equals with 2 var in ngFilter. I try lot of combinaison, but it doesn't work...
I get the first var here, in $routeParams
function ANNAnnonceListe(DBManager, $routeParams) {
var ici = this;
this.annonces_ar = [];
this.idLieu = $routeParams.idLieu;
DBManager.all('ANNAnnonce')
.then(function (annonces) {
console.log(annonces);
ici.annonces_ar = annonces;
})
.catch(function (error) {
console.log(error)
});
}
the second here :
<div ng-repeat="(key, value) in vm.annonces_ar | filter: {value.id : vm.idLieu_nb} ">
{{value.nom_str}}
{{value.idLieu_nb}}
</div>
i would like this equality (vm.idLieu = value.id_nb) and display if is true
Thanks.
It should be property name directly id_nb instead of value.id. Also have : true parameter at the end of your filter to perform exact check, inspite of contains check.
<div ng-repeat="(key, value) in vm.annonces_ar | filter: { idLieu_nb : vm.idLieu}: true ">
{{value.nom_str}}
{{value.idLieu_nb}}
</div>

AngularJS: Update all objects in $scope simultaneously

Say I have an object stored in $scope like so:
$scope.todo = [
{
"title" : "Groceries",
"todoItems" : [
{
"title" : "Milk",
"status" : "Not Done"
},
{
"title" : "Eggs",
"status" : "Not Done"
},
{
"title" : "Bread",
"status" : "Done"
}
]
},
{
"title" : "Medical",
"todoItems" : [
{
"title" : "Make eye doctor appointment",
"status" : "Not Done"
},
{
"title" : "Go to pharmacy",
"status" : "Not Done"
},
{
"title" : "Take vitamins",
"status" : "Done"
}
]
}
];
I am creating a feature that allows inline editing of each todo item, like so:
I achieve this by toggling a property on the todo list item called editMode. See lines 11-14 in the following code block:
<div ng-app="myApp">
<div ng-controller="dashBoard">
<div class="panel panel-default list-[(listID)]" ng-repeat="(listID, todoList) in todo" ng-cloak>
<div class="panel-heading">[( todoList.title )]</div>
<ul class="list-group">
<li ng-repeat="(itemID, todoItem) in todoList.todoItems" data-as-sortable="board.dragControlListeners" data-ng-model="items" class="status-[(todoItem.status)] todo-item todo-item-[(itemID)]" data-as-sortable-item>
<div class="input-group">
<span data-as-sortable-item-handle class="input-group-addon">
<input ng-click="toggleStatus(listID, itemID, todoItem.status)" type="checkbox" ng-checked="todoItem.status == 1">
</span>
<span ng-if="!todoItem.editMode" class="todo-item-label-wrapper">
<div ng-click="toggleEditMode(listID, itemID, 1)" class="todo-item-label">[(todoItem.value)]</div>
</span>
<span ng-if="todoItem.editMode" class="todo-input-wrapper">
<input show-focus="todoItem.editMode" ng-keyup="$event.keyCode == 13 && toggleEditMode(listID, itemID, 0)" type="text" ng-model="todoItem.value" class="form-control">
</span>
</div>
</li>
</ul>
</div>
</div>
</div>
When any given todo item is clicked, it goes into edit mode. The todo item stays in edit mode until the user hits enter. I'd like to make it impossible to have multiple todo items in edit mode at the same time. If you click on todo item "foo" and then click on todo item "bar", todo item "foo" should switch back to read-only mode.
I am currently achieving this by individually switching every todo item with angular.forEach(), e.g.:
$scope.toggleEditMode = function(listID, itemID, editMode) {
$scope.todo[listID].todoItems[itemID].editMode = editMode;
//Turn off edit mode on every todo item other than the one that was just clicked
angular.forEach($scope.todo[listID].todoItems, function(todoItem, foreignItemID) {
if (foreignItemID !== itemID) {
$scope.todo[listID].todoItems[foreignItemID].editMode = 0;
}
});
}
But I wonder if angular has some utility for this usecase that I should be using.
What I do in such a case is not having an editMode property on each item, but instead using a scope variable like $scope.currentEditItemId. Then you do something like this:
$scope.toggleEditMode = function (listID, itemID, enableEdit) {
if (enableEdit === 1) {
$scope.currentEditItemId = itemId;
// ... whatever you need to do here
}
}
And the HTML would look like this:
<span ng-if="itemId != currentEditItemId" class="todo-item-label-wrapper">
<div ng-click="toggleEditMode(listID, itemID, 1)" class="todo-item-label">[(todoItem.value)]</div>
</span>
<span ng-if="itemId == currentEditItemId" class="todo-input-wrapper">
<input show-focus="todoItem.id == currentEditItemId" ng-keyup="$event.keyCode == 13 && toggleEditMode(listID, itemID, 0)" type="text" ng-model="todoItem.value" class="form-control">
</span>
Revisiting this a bit, I realize one way you could update everything in the scope simultaneously is to instantiate each item in $scope through a constructor, and then update the constructor prototype. This doesn't really solve for the original use case I posed above (which is perhaps better stated as "update all items in scope except one") but I think it still has some useful applications.
So, if you want to have an item which, when clicked, updates lots of other items, you could do something like this:
HTML:
<div ng-app="myApp" ng-controller="toDo">
<div ng-click="toggleEdit(index)" ng-class="{{item.editable}}" ng-repeat="(index, item) in items"> {{item.title}} </div>
</div>
JS:
var app = angular.module('myApp', []);
function newItem(title) {
this.title = title;
}
newItem.prototype.editable = 'foo';
function toggleAll() {
newItem.prototype.editable = 'bar';
}
app.controller('toDo', function($scope) {
$scope.items = []
for (var i = 0; i <= 10; i++) {
var item = new newItem("item" + i);
$scope.items.push(item);
}
$scope.toggleEdit = function(index) {
toggleAll();
}
});
Result:
Here we see the class foo toggle to the class bar on all items when any given item is clicked:

Meteor:search-source doesn´t clear the search

I´m having some troubles with Meteor Search-source. The pagackage works fine, but I think that it has big leaks in the documentation. My problem right now is that I can´t clear the search when I don´t type any in the search field.
Currently the App show a list of websites. If I looking for some web in the search field, the App show me a list with results. But when I delete the characters in the field text (empty search), the list with results doesn´t disappear. It show the complete list of elements instead of show a empty list.
I have tried a lot of solutions but nothing works...
You can test typing for example "coursera" in the search field in my app, and next delete all types to check it out.
Some suggestion? Many thanks in advance
My App
SERVER
SearchSource.defineSource('items', function(searchText, options) {
var options = {sort: {upvote: -1}, limit: 20};
// var options = options || {};
if(searchText) {
var regExp = buildRegExp(searchText);
/*var selector = {title: regExp, description: regExp};*/
var selector = {$or: [
{title: regExp},
{description: regExp}
]};
return Websites.find(selector, options).fetch();
} else {
return Websites.find({}, options).fetch();
}
});
function buildRegExp(searchText) {
var words = searchText.trim().split(/[ \-\:]+/);
var exps = _.map(words, function(word) {
return "(?=.*" + word + ")";
});
var fullExp = exps.join('') + ".+";
return new RegExp(fullExp, "i");
}
CLIENTE
//search function
var options = {
keepHistory: 1000 * 60 * 5,
localSearch: true
};
var fields = ['title','description'];
itemSearch = new SearchSource('items', fields, options);
//end search function
//search helper
Template.searchResult.helpers({
getItems: function() {
return itemSearch.getData({
transform: function(matchText, regExp) {
return matchText.replace(regExp, "$&")
},
sort: {upvote: -1}
});
},
isLoading: function() {
return itemSearch.getStatus().loading;
}
});
// search events
Template.searchBox.events({
'keyup #search-box': _.throttle(function(e) {
var text = $(e.target).val().trim();
console.log(text);
itemSearch.search(text,{});
}, 200)
});
HTML
<template name="searchResult">
<div class="container">
<div class="jumbotron searchResult">
<h3> Search results </h3>
<ol>
{{#each getItems}}
{{> website_item_search}}
{{/each}}
</ol>
<!--<div id="search-meta">
{{#if isLoading}}
searching ...
{{/if}}
</div>-->
</div>
</div>
</template>
Just by changing the code on server file, you should be able to see no results on blank text field.
Here is new code. https://github.com/ashish1dev/search_source_example
SearchSource.defineSource('packages', function(searchText, options) {
var options = {sort: {isoScore: -1}, limit: 20};
if(searchText.length>=1) {
var regExp = buildRegExp(searchText);
var selector = {$or: [
{packageName: regExp},
{description: regExp}
]};
return Packages.find(selector, options).fetch();
} else if (searchText.length===0){
return [];// return blank array when length of text searched is zero
}
else {
return Packages.find({}, options).fetch();
}
});

Using AngularJS to create an instant search by querying an array

This is going to be a rather longwinded question, so please bear with me...
I have an array of about 25-30 items. They are sorted through various filters such as brand, type, material, size, etc.. How can I go about building a searchable filter. All of the ones I've seen just include a filter:query | in their filters. However I can't get mine to query my existing array.
Here is what my array looks like, only going to show 1 item to keep size down..
$scope.products = [
{
src: 'images/img/image1.jpg',
name: 'XXX-1A',
brand: 'Brand A',
material: 'dry',
size: '00',
type: 'dry pipe',
color:'red'
}];
Function for filtering (only included 1 to save space):
$scope.brandIncludes = [];
$scope.includeBrand = function(brand) {
var i = $.inArray(brand, $scope.brandIncludes);
if (i > -1) {
$scope.brandIncludes.splice(i, 1);
} else {
$scope.brandIncludes.push(brand);
}
}
$scope.brandFilter = function(products) {
if ($scope.brandIncludes.length > 0) {
if ($.inArray(products.brand, $scope.brandIncludes) < 0)
return;
}
return true;
}
This is what I am using to filter from the HTML, I am using checkboxes to select each filter:
<div class="info" ng-repeat="p in products |
filter:brandFilter |
filter:materialFilter |
filter:typeFilter |
filter:styleFilter">
</div>
My search bar mark up:
<div class="filtering">
<div class="search-sect">
<input name="dbQuery" type="text" placeholder="Search pieces" class="search-input" ng-model="query"/>
</div>
One of the filter's mark up:
<input type="checkbox" ng-click="includeStyle('adaptor')"/>Adaptor<br>
Now that you have all the code, here are some of the things I've tried that don't seem to be running right:
My Attempt:
Search bar:
<input type="text" id="query" ng-model="query"/>
Filter:
<li ng-repeat="p in products | filter:query | orderBy: orderList">
I understand that to some experienced with angular, this is a relatively easy task, but I am just learning and can't seem to wrap my head around searching a query. It's probably a simple solution that I am overlooking. This is my first Angular app and I am trying to bite off more than I can chew in order to learn more.
I appreciate all responses, thanks in advance!
As per request: CodePen
The simple built-in angular filter is not smart enough to to work with your checkbox design, so try writing a custom filter. You will need to bind the checkboxes you mentioned to variables in your scope, e.g. brandFilterIsEnabled. See the tutorial for writing custom filters. Here is a working example.
var myApp = angular.module('myApp', []);
myApp.controller('ctrl', function ($scope) {
$scope.items = [{
name:'foo',
color:'red'
},{
name:'bar',
color:'blue'
},{
name:'baz',
color:'green'
}];
$scope.searchNames = true;
$scope.searchColors = true;
$scope.$watch('searchColors', function(){
$scope.searchKeys = [ $scope.searchNames ? 'name' : null, $scope.searchColors ? 'color' : null ];
});
$scope.$watch('searchNames', function(){
$scope.searchKeys = [ $scope.searchNames ? 'name' : null, $scope.searchColors ? 'color' : null ];
});
});
myApp.filter('advancedSearch', function($filter) {
return function(data, keys, query) {
results = [];
if( !query ){
return data;
} else {
angular.forEach( data, function( obj ){
var matched = false;
angular.forEach( keys, function( key ){
if( obj[key] ){
// match values using angular's built-in filter
if ($filter('filter')([obj[key]], query).length > 0){
// don't add objects to results twice if multiple
// keys have values that match query
if( !matched ) {
results.push(obj);
}
matched = true;
}
}
});
});
}
return results;
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<html ng-app="myApp">
<div ng-controller="ctrl">
<input type='checkbox' ng-model='searchNames'>search names</input>
<input type='checkbox' ng-model='searchColors'>search colors</input>
<input type='text' ng-model='query'>search objects</input>
<ul>
<li ng-repeat="item in items | advancedSearch : searchKeys : query">
<span style="color:{{item.color}}">{{item.name}}</span>
</li>
</ul>
</div>
</html>

i10n angularjs assign translate variable

Okay so i have the following small translation file:
{
"components" : {
"1" : "Video",
"2" : "Lyd",
"3" : "Dokument",
"4" : "Tekst"
}
}
And then i have the following li item:
<li ng-repeat="type in componentTypes" ng-hide="module.module_type_id == 2 || module.module_type_id == 10">{{type.name}}</li>
What you need to notice is :
{{type.name}}
Or more precisely:
translate="components.{{1}}"
With this it does not translate the <a></a> tag.
However if i do
translate="components.1"
it translates correctly however this method doesnt work for me
so my question is how can dynamicly change the value of a the translate attribute?
The reason components.{{1}} is not working is because the double curlies in Angular is just meant to evaluate an expression. 1 is just a number, so you'll get components.1 everytime.
If I understand what you need correctly, you need to have a corresponding component based on type. So if type.id === 1 then your type is Video.
In order to achieve that in Angular dynamically, you should just have:
translate="{{components[type.id]}}"
Fiddle
As far as I understand you want to dynamize the translation json if added a new type to your componentTypes array.
There is a solution for that need, you can implement a new custom translation loader factory and use it with specified way here https://github.com/angular-translate/angular-translate/wiki/Asynchronous-loading. After that you have to add this new item to the translation json, your array and then refresh the translation.
View:
<div ng-app="myApp">
Links to jsfiddle.net must be accompanied by code. Please indent all code by 4 <div ng-controller="MyCtrl">
<input type="text" ng-model="type.name" /> Add Component
<ul>
<li ng-repeat="type in componentTypes" ng-hide="module.module_type_id == 2 || module.module_type_id == 10">{{ 'components.' + type.name | translate }}</li>
</ul>
</div>
</div>
Implementation of your application:
var myApp = angular.module('myApp', ['pascalprecht.translate']);
var components_en = {
"components": {
"1": "Video",
"2": "Lyd",
"3": "Dokument",
"4": "Tekst"
}
};
myApp.config(function ($translateProvider) {
$translateProvider.useLoader('customLoader', {});
$translateProvider.translations('en', components_en);
$translateProvider.preferredLanguage('en');
});
myApp.controller('MyCtrl', function ($scope, $translate) {
$scope.module = {
module_type_id: 1
};
$scope.addComponent = function (type) {
// Add the componentTypes array you took from database
$scope.componentTypes.push({
name: $scope.componentTypes.length + 1
});
// Add the translation object
components_en["components"][$scope.componentTypes.length] = type.name;
console.log(components_en);
$translate.refresh();
};
$scope.componentTypes = [{
name: 1
}, {
name: 2
}, {
name: 3
}, {
name: 4
}];
});
myApp.factory('customLoader', function ($http, $q) {
return function (options) {
var deferred = $q.defer();
deferred.resolve(components_en);
return deferred.promise;
}
})
I prepared a demonstration for that like usage, please below jsfiddle:
http://jsfiddle.net/nerezo/1z071wzg/6/
Note: This like translation modifications will not be persistent and new translations will be lost.
Please give this a try:
translate="{{'components.' + type.id}}" //if there is id in type
or
translate="{{'components.' + ($index + 1)}}"

Categories