AngularJS Filter expression not being updated - javascript

I have am dealing with two HTML files and one controller. In one HTML file I have a search bar:
<a ui-sref="allSubmissions">
<input id="searchBar" ng-controller="submissions" ng-model="searchString" class="searchBar" type="text" placeholder="Search">
</a>
Along with some other stuff. In a separte HTML file, I have a list of submissions that I populate with ng-repeat:
<div class="container" ng-controller="submissions">
<ul class="subsList">
<ul>
<li ng-repeat="item in submissionCollectionTest | filter: searchString" class="submissionDivider hover">
<span class="ageText">{{item.age}}</span>
<span class="submissionsText">{{item.submission}}</span>
<span class="descriptionText">{{item.description}}</span>
</li>
</ul>
</ul>
</div>
Along with some other code. It makes sense in my complete app that these files be separate, however, I cannot get the search input to tie to the filter expression and automatically update.
In my controller, i have the following scope variable:
myApp.controller('submissions',function($scope){
$scope.searchString = '';
When I click in the search bar, it takes me to the new page with shows all the content populated from the ng-repeat, then I want to filter the content by typing in the search bar. This does work when I have the search bar code in the same place as the content. How can I update "searchString" globally so that the filter responds when it changes? Thanks

You could use a broadcast from one controller and listen in another. If you're using multiple routes, then you'll want to store the search term in a service.
Example: https://jsfiddle.net/h0bd88dc/
function SearchSubmissionsCtrl($rootScope,Service) {
var vm = this;
vm.searchString = Service.searchString;
vm.search = search;
function search() {
Service.searchString = vm.searchString;
$rootScope.$broadcast('search', vm.searchString);
}
}
function SubmissionsCtrl($scope,Service) {
var vm = this;
vm.searchString = Service.searchString;
vm.items = [{
age: 22,
submission: 'Yes',
description: 'Yo'
}, {
age: 5,
submission: 'Derp',
description: 'Hey'
}];
$scope.$on('search', function(e, string) {
vm.searchString = string;
})
}
function Service() {
var service = {
searchString: null
};
return service;
}

Related

Dynamic HTML partial based on REST response

I have an application I am building using an Angular JS front end and a REST-based API back end feeding a MySQL database. There are REST calls made from the front end to the back end to populate or retrieve data in the database. I want to add a drop down selection box to my angular JS front end home page. I want the selection to trigger a REST call to the database, to retrieve a specific value and have that value become a part of a dynamically loaded html partial.
As an example, the drop down would select a model of a car (Toyota Corolla, Honda Accord, etc.) When you select that model, the controller would make a REST call to the appropriate table(s) to get the rest of the information for that car (MPG, size, weight, etc.) Once it did this, it would load a partial HTML on the page that was a template HTML file but with dynamic content. So the page loaded would be /#/carInfo?toyotaCorolla. The template partial html file would load and then the tables on the template would populate with the response from that REST call. So I would essentially have a single template for that page, but it would call a new VERSION of the page based on what was selected.
I am thinking about this in my head and I do not have my application code with me. This question is not for the actual code solution, but for someone to either write up some pseudo code or point me to a demo/example online that is similar to this...if it is even possible. I am doing searches on my own, but I may be searching for the wrong terminology to get this accomplished. Any pointers or help on this would be appreciated.
UPDATE:
Now that I am home, here is a snippet of the code I am having issues with.
<ul class="nav navbar-nav">
<li></li>
<li class="dropdown">
<a href="javascript:void(0)" data-target="#" class="dropdown-toggle" data-toggle="dropdown">
Select a car...
<b class="caret"></b></a>
<ul class="dropdown-menu">
<li ng-model="selectedCar.value" ng-repeat="x.car for x in cars"
ng-change="selectedCarChanged()"></li>
</ul>
</li>
</ul>
That is not populating correctly. I have the same ng code for a <select> implementation using ng-options instead of ng-repeat. I was hoping it would be a simple transition, but the CSS version using the lists is not working.
Please find the code snippet below. Hope this will be helpful
car-list.html
<div ng-controller="carListController">
<select ng-model="selectedCar" ng-change="onSelectCar(selectedCar)">
<option ng-repeat="car in cars">{{car}}</option>
</select>
</div>
carListController.js
app.controller('carListController', function($scope, $location) {
$scope.carList = ['Honda', 'Toyota', 'Suzuki', 'Hyundai'];
$scope.onSelectCar = function(car) {
$location.path('#/carInfo').search({carInfo: car});
}
});
carInfo.html
<div class="carDetails">
<span>Car Name: {{car.name}}</span>
<span>Car Model: {{car.model}}</span>
<span>Car Year: {{car.year}}</span>
<span>Car Size: {{car.size}}</span>
</div>
carInfoDetailsController.js
app.controller('carInfoController', function($scope, $location, $http) {
$scope.car = {};
$scope.init= function() {
$http.get('url/' + $location.search('carInfo'), function(response) {
$scope.car = response;
});
};
$scope.init();
});
appConfig.js
app.config(function($routeProvider){
$routeProvider.when('/carInfo'{
templateUrl: "carInfo.html",
controller: "carInfoController"
});
});
something like:
//in a service
(function() {
function MyService($http) {
var myService = {};
MyService.accessMultiTool = function(){
var args = Array.from(arguments);
var method, url, authorization;
args.forEach(function(item){
if('method' in item){
method = item.method;
}else if ('url' in item){
url = item.url;
}else if ('authorization' in item){
authorization = item.authorization;
}
});
delete $http.defaults.headers.common['X-Requested-With'];
return $http({
method: method,
origin: 'http://someclient/',
url: url,
headers: {'Authorization': authorization}
}).error(function(status){generate some error msg});
};
return MyService;
}
angular
.module('myApp')
.factory('MyService', ['$http', MyService]);
})();
//in a controller
(function () {
function MyCtrl(MyService) {
var myController = this;
this.car_model_options = ["Honda", "Chevy", "Ford", "Nissan"];
this.bound_car_model_obj = {
model: null
};
this.getCarModel = function(){
MyService.accessMultiTool({method: 'GET'}, {url: 'http://somebackend/api/cars/' + myController.bound_car_model_obj.model}, {authorization: this.activeMember.auth}).then(function(data){
myController.setCurrCarModel(data);
});
this.setCurrCarModel = function(data){
myController.currently_selected_car_model = data;
};
};
};
angular
.module('myApp')
.controller('MyCtrl', ['MyService', MyCtrl]);
})();
//in a template
<div ng-controller="MyCtrl as mycontroller">
<select data-ng-init="this.bound_car_model_obj.model = mycontroller.car_model_options[0]" data-ng-model="this.bound_car_model_obj.model" data-ng-options="option for option in mycontroller.car_model_options" >
</select>
<table>
<tr ng-repeat="car in mycontroller.currently_selected_car_model>
<td>{{car.someproperty}}>/td>
<td>{{car.someotherproperty}}>/td>
</tr>
</table>
</div>

ng-repeat not rendering data from array

I am working on a web app where non-profit organizations can create a profile and be easily searchable by various parameters. In the "create and organization" form, I have a nested array where the organization can add donations that they need. The array is storing ok (I can add multiple donations), however when I try to display it using ng-repeat, nothing renders. When I don't use the ng-repeat and just display via {{ ctrl.organization.donations }} the information shows up with brackets and quotation marks.
Here is the code that I use to add the donations (via the newOrganization controller):
function NewOrganizationController(OrganizationService, CategoryService, $stateParams, $state, $http, Auth){
var ctrl = this;
CategoryService.getCategories().then(function(resp) {
ctrl.categories = resp.data;
});
ctrl.donations = [{text: ''}];
Auth.currentUser().then(function(user) {
ctrl.user = user;
})
ctrl.addNewDonation = function() {
var newDonation = ctrl.donations.length+1;
ctrl.donations.push({text: ''});
};
ctrl.removeDonation = function() {
var lastItem = ctrl.donations.length-1;
ctrl.donations.splice(lastItem);
};
ctrl.addOrganization = function() {
var donations = this.donations;
var allDonations = [];
for (var key in donations) {
if (donations.hasOwnProperty(key)) {
var donation = donations[key].text;
allDonations.push(donation);
}
}
var data = {
name: ctrl.organization.name,
description: ctrl.organization.description,
address: ctrl.organization.address,
donations: allDonations.join("/r/n"),
category_id: this.category.id
};
OrganizationService.createOrganization(data);
$state.go('home.organizations');
};
}
angular
.module('app')
.controller('NewOrganizationController', NewOrganizationController);
Here is the code that I am using to display the array on my show page (this is what shows up with brackets, i.e. donations needed: ["food", "clothing"]):
<h5>{{ ctrl.organization.donations }}</h5>
This is the ng-repeat code that is not rendering anything to the page:
<li class="list-group-item" ng-repeat="donation in donations track by $index">
{{ donation }}
</li>
I've tried to use .join(', ') within the {{donation}} brackets, but this isn't recognized as a function.
edit: After taking AJ's suggestion here is a screenshot of what appears...anyone know how to fix this?
seems that my array is showing up in table form, with each row containing one character
Any help would be greatly appreciated. Here is a link to the github repo in case you want to look at anything else or get a bigger picture.
You need to use the same variable name that works in the h5
<li class="list-group-item" ng-repeat="donation in ctrl.organization.donations track by $index">
{{ donation }}
</li>

collect filtered ng-repeat data to use in controller

I am trying to build a picture library in a responsive grid with data coming down from a Mongo DB. Data from db is in this form. name, image, keyword, bio, reference", searchable,
I start with an ng-repeat to display category selection checkboxes based on the picture keywords. This seems to work fine.
p CATEGORIES:
span.formgroup(data-ng-repeat='category in mongoController.catData')
input.checks( type='checkbox', ng-model='mongoController.filter[category]')
| {{category}}
Here is the factory that sorts and identifies the keyword checkboxes:
getCategories: function (cat) {
return (cat || []).map(function (pic) {
return pic.keyword;
}).filter(function (pic, idx, array) {
return array.indexOf(pic) === idx;
});
}
From there I filter an ng-repeat to display pictures based on the checkbox selection, and/or a search field which works well also.
input.form-control.input-med(placeholder="Search" type='text', ng-model="search.searchable")
br
div.light.imageItem(data-ng-repeat="picture in filtered=( mongoController.allData | filter:search | filter:mongoController.filterByCategory)")
a(data-toggle="modal", data-target="#myModal", data-backdrop="static", ng-click='mongoController.selectPicture(picture)')
img( ng-src='../images/{{picture.image}}_tn.jpg', alt='Photo of {{picture.image}}')
p Number of results: {{filtered.length}}
Functions to display picture lists:
//Returns pictures of checkboxes || calls the noFilter function to show all
mongoController.filterByCategory = function (picture) {
return mongoController.filter[picture.keyword] || noFilter(mongoController.filter);
};
function noFilter(filterObj) {
for (var key in filterObj) {
if (filterObj[key]) {
return false;
}
}
return true;
}
If you click one of the pictures a modal box pops up with all of the input fields where you can edit image specific fields.
The part I am really struggling with is how to gather the usable data from the filtered ng-repeat to use in the controller so when the modal box is up I can scroll right or left through the other pictures that met the criteria of the search.
Any suggestions would help, even why the hell are you doing it this way?
When you declare the ng-repeat that filters the pictures, your filtered variable belongs to the current $scope (which I cannot infer from the question, as it stands). You could associate the filtered variable to a specific controller by using Controller As syntax: (i.e. using <elem ng-repeat="picture in ctrl.filtered = (ctrl.data | filter1 | filterN)"/>)
Example: (also in jsfiddle)
var mod = angular.module('myapp', []);
mod.controller('ctrl', function() {
var vm = this;
vm.data = ['alice', 'bob', 'carol', 'david', 'ema'];
vm.onbutton = function() {
// access here the filtered data that mets the criteria of the search.
console.log(vm.filtered);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myapp" ng-controller="ctrl as vm">
<input ng-model="search" type="text" />
<p data-ng-repeat="item in vm.filtered = (vm.data | filter:search)">
{{item}}
</p>
<p>Items: {{vm.filtered.length}}</p>
<button ng-click="vm.onbutton()">Show me in console</button>
</div>

binding knockout observable arrays to multiple <ul> elements

For a navigation menu, I have two groups of links, each group and link showing up or not dependent on a user's role. The roles are looked up when the link structure is being built and the list of links is built accordingly. The returned JSON gets parsed, put into observable arrays with no problem, but when I actually try and apply the bindings, the binding fails because the observables are blank. Here is the HTML...
<ul id="user-menu" class="menu" data-bind="foreach: areas">
<li>
<a data-bind="attr: { href: areaLink }">
<img data-bind="attr: { src: iconUri }" />
<span data-bind="text: areaName"></span>
</a>
</li>
</ul>
<ul id="admin-menu" class="menu" data-bind="foreach: adminAreas">
<li>
<a data-bind="attr: { href: areaLink }">
<img data-bind="attr: { src: iconUri }" />
<span data-bind="text: areaName"></span>
</a>
</li>
</ul>
Knockout view model in the background...
var navigation = (function() {
function Area() {
var self = this;
self.areaName = ko.observable();
self.areaLink = ko.observable();
self.iconUri = ko.observable();
self.sequenceNo = ko.observable();
self.isAdmin = ko.observable();
self.loadFromVM = function (vm) {
self.areaName(vm.name || '');
self.areaLink(vm.link || '');
self.iconUri(vm.iconUri || '');
self.sequenceNo(vm.sequenceNo || '');
self.isAdmin(vm.isAdmin);
}
}
function viewModel() {
var self = this;
self.areas = ko.observableArray([]);
self.adminAreas = ko.observableArray([]);
self.setup = function () {
var data = {}; // population with basic session data
$.getJSON('....php', { JSON.stringify(data) }, function (results) {
for (var i = 0; i < results.length; i++) {
var area = new Area();
area.loadFromVM(results[i]);
if (area.isAdmin()) {
self.adminAreas().push(area);
} else {
self.areas().push(area);
}
}
});
};
}
var vmInstance;
return {
setup: function () {
vmInstance = new viewModel();
vmInstance.setup();
ko.applyBindings(vmInstance, $('#user-menu')[0]);
ko.applyBindings(vmInstance, $('#admin-menu')[0]);
}
};
})();
And then I bring it together with this in the navigation view file...
navigation.setup();
So after I get my JSON back, parse it, and organize it when I loop through the success function of the $.getJSON method, putting a watch on self.areas() and self.adminAreas() does show that the arrays have the exact information I want them to. But by the time they have to be applied, calling vmInstance.areas().length or vmInstance.adminAreas().length returns zero. Even more oddly, putting in an alert with the length of the arrays right after the $.getJSON call but within the setup() function will cause the alert to fire first, show zeroes, then goes through the get, populates the array, then fires zeroes again.
Not exactly sure what's going on here, but I can't remember seeing this kind of behavior in another project so I'm not quite sure what I'm doing wrong here. Any ideas or advice would be greatly appreciated.
EDIT: Nevermind on the Fiddle. It doesn't really capture my actual error.
adminarea object is not initialized.you made the adminArea variable but instead of this you have used same area variable to set values.
var adminArea = new Area();
adminArea.areaName('test admin area');
adminArea.areaLink('#');
adminArea.iconUri('http://evernote.com/media/img/getting_started/skitch/windows8/win8-checkmark_icon.png');
adminArea.sequenceNo(1);
adminArea.isAdmin(true);
Fiddle Demo

How to properly bind text box value back to model in a observable array?

I have a model that has an observable array, I can display the data in a text box, but I can't figure out how to bind it back to the original array.
Here is the working sample I have.
<ul data-bind='foreach: frameworks'>
<li>
<button class='btn' value='pick me'
data-bind='text: name, click: $parent.selectFramework'>
</button>
</li>
</ul>
<input type='text' data-bind='value: selectedFramework().name' />
<pre data-bind='text: ko.toJSON($root.selectedFramework, null, 4)'>
</pre>
var Framework = {
name: ''
};
var App = new function () {
var self = this;
self.frameworks = ko.observableArray();
self.selectFramework = function (item) {
self.selectedFramework(item);
};
self.selectedFramework = ko.observable(Framework);
};
App.frameworks([{name: 'foo'}, {name: 'bar'}]);
ko.applyBindings(App);
You are almost there. You need to make the 'name' properties on each framework observable. I have updated your JsFiddle here
App.frameworks([{
name: ko.observable('foo')
}, {
name: ko.observable('bar')
}]);
The value is only stored in your selectedFramework observable, so you would be able to access it via App.selectedFramework(). The observable doesn't take any variable and make it observable, it will store whatever value you pass it internally. If you want to update the external Framework variable, you would do that in your selectFramework function.
self.selectFramework = function (item) {
self.selectedFramework(item);
Framework = item;
};

Categories

Resources