I have two objects. First one have entire school full details of students record. Example like
var first = {
students:
[
{ id:'1', name:"suresh", age:"20", degree:"BSc", status:"obsent"},
{ id:'2', name:"ramesh", age:"21", degree:"BCom", status:"present"},
{ id:'3', name:"rajesh", age:"19", degree:"BA", status:"leave"},
{ id:'4', name:"satish", age:"28", degree:"BL", status:"obsent"}
]
}
Second one have particular class students information about the status of the student for that day. Example like
var second ={
students:
[
{ id:'1',status:"present"},
{ id:'12',status:"obsent"},
{ id:'3',status:"obsent"},
{ id:'14',status:"leave"}
]
}
Now I need to compare the student id and need to display the status based on the result. I have achieved in the following way.
items = first.students.map(function(item){
status =item.status;
second.students.map(function(key){
if(key.id == item.id) { status = key.status }
});
return "<tr><td>"+item.name+"</td><td>"+item.age+"</td><td>"+item.degree+"</td><td>"+status+"</td></tr>";
});
$('table#main tbody').html(items);
The above code is working fine. But if you look at my code, I have used the map functionality multiple times. I feel that I have done something wrong in the performance wise. Is that possible to reduce using the map twice or any other better way to achieve the same result. Please suggest me.
Code Snippet
var first = {
students:
[
{ id:'1', name:"suresh", age:"20", degree:"BSc", status:"obsent"},
{ id:'2', name:"ramesh", age:"21", degree:"BCom", status:"present"},
{ id:'3', name:"rajesh", age:"19", degree:"BA", status:"leave"},
{ id:'4', name:"satish", age:"28", degree:"BL", status:"obsent"}
]
}
var second ={
students:
[
{ id:'1',status:"present"},
{ id:'12',status:"obsent"},
{ id:'3',status:"obsent"},
{ id:'14',status:"leave"}
]
}
items = first.students.map(function(item){
status =item.status;
second.students.map(function(key){
if(key.id == item.id) { status = key.status }
});
return "<tr><td>"+item.name+"</td><td>"+item.age+"</td><td>"+item.degree+"</td><td>"+status+"</td></tr>";
});
$('table#main tbody').html(items);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="main" cellspacing="2" border="1">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Degree</th>
<th>Stauts</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</tbody>
</table>
Due to the way your objects are set up, it looks like that will be O(n) time for the lookup, because you need to loop through the first student array for every student id.
To get around this, you can do a single map where you assign the id as the key of a new intermediate object in the format of:
x = {1: {...}, 2: {...}}
From there, you can now do constant time O(1) lookups:
x[id]
The only extra work is building the intermediate hash, but that will be less computation than what you have above.
See this example below. Note that it does use 2 maps, but it's different than your example because it's not a map within a map which is exponential:
var students = [
{ id:'1', name:"suresh", age:"20", degree:"BSc", status:"obsent"},
{ id:'2', name:"ramesh", age:"21", degree:"BCom", status:"present"},
{ id:'3', name:"rajesh", age:"19", degree:"BA", status:"leave"},
{ id:'4', name:"satish", age:"28", degree:"BL", status:"obsent"}
];
var studentIds = {};
students.forEach(function(student) {
studentIds[student.id] = {name: student.name, age: student.age, degree: student.degree, status: student.status}
});
var second = [
{ id:'1',status:"present"},
{ id:'12',status:"obsent"},
{ id:'3',status:"obsent"},
{ id:'14',status:"leave"}
];
var studentStatuses = second.map(function(student) {
// do whatever you have to do here
return (studentIds[student.id] || {}).status;
});
The complexity will be better if you build an object which keys are id and values are status from second.students then you update status in first.students based on this object:
var first = {
students:
[
{ id:'1', name:"suresh", age:"20", degree:"BSc", status:"obsent"},
{ id:'2', name:"ramesh", age:"21", degree:"BCom", status:"present"},
{ id:'3', name:"rajesh", age:"19", degree:"BA", status:"leave"},
{ id:'4', name:"satish", age:"28", degree:"BL", status:"obsent"}
]
}
var second ={
students:
[
{ id:'1',status:"present"},
{ id:'12',status:"obsent"},
{ id:'3',status:"obsent"},
{ id:'14',status:"leave"}
]
}
var statusById= second.students.reduce(function(m, e) {
m[e.id] = e.status;
return m;
}, {});
items = first.students.map(function(item){
item.status = statusById[item.id] || item.status;
return "<tr><td>"+item.name+"</td><td>"+item.age+"</td><td>"+item.degree+"</td><td>"+item.status+"</td></tr>";
});
$('table#main tbody').html(items);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="main" cellspacing="2" border="1">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Degree</th>
<th>Stauts</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</tbody>
</table>
I have a data setup which looks like this
$scope.friends = [
{
name: function() {
return 'steve';
},
number: function() {
return 555;
}
},
{
name: function() {
return 'mary';
},
number: function() {
return 555;
}
},
{
name: function() {
return 'jo';
},
number: function() {
return 888;
}
}
]
Now what I want is to filter data on ng-repeat based on text input.To achieve this I have tried like this:
<table id="searchTextResults">
<tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friend in friends | filter:search">
<td>{{friend.name}}</td>
<td>{{friend.number}}</td>
</tr>
</table>
Where input is defined like <input type="text" ng-model="search.name"/>.
But the filter does not work.I have tried and come to see if I modify the data structure like this
$scope.friends = [
{
name:'steve',
number: 555
},
{
name:'marry',
number: 555
},
{
name:'steve',
number: 888
}
]
then only this works.However I want to keep my data structure as it is.
Can anyone please help on this how can I filter having the above data structure.
plnkr example.
You can modify the friends data to achieve this requirement.
What I have done is using angular.forEach() function to modify the data and put it into a new array called $scope.modifiedFriends.
$scope.modifiedFriends = [];
angular.forEach($scope.friends, function(friend) {
$scope.modifiedFriends.push({
name: friend.name(),
number: friend.number()
});
});
Instead of ng-repeat on friends, I ng-repeat of modifiedFriends.
<tr ng-repeat="friend in modifiedFriends | filter:search">
<td>{{friend.name}}</td>
<td>{{friend.number}}</td>
</tr>
Working Plunker
I have a table with these fields: product, lot, input1, input2. You can clone a line, and you can add a new line.
What I want to do is that for each row you can add a new Lot created by a "number" and by "id" that user write in the input field under the Select lot. And I wanted that the script add the new Lot in the json data and the lot 's option list.
This is the function for add that I tried to do:
$scope.addLot = function() {
var inWhichProduct = row.selectedProduct;
var newArray = {
"number": row.newLot.value,
"id": row.newLot.id
};
for (var i = 0; i < $scope.items.length; i++) {
if ($scope.items[i].selectedProduct === inWhichProduct) {
$scope.items[i].selectedLot.push(newArray);
}
}
};
-->> THIS <<-- is the full code.
Can you help me?
I think your question is a little too broad to answer on Stack Overflow, but here's an attempt:
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<tr ng-repeat="lot in lots">
<td>{{ lot.id }}</td>
<td>{{ lot.name }}</td>
</tr>
</table>
<p>name:</p> <input type="text" ng-model="inputName">
<p>id:</p> <input type="text" ng-model="inputId">
<button ng-click="addLotButton(inputId, inputName)">Add</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.lots = [{
name: "test",
id: 1
},
{
name: "test2",
id: 2
}
];
$scope.addLot = function(lotId, lotName) {
var newLotObject = {
name: lotName,
id: lotId
};
$scope.lots.push(newLotObject);
};
$scope.addLotButton = function(id, name) {
$scope.addLot(id, name);
};
$scope.addLot(3, "Another test");
});
</script>
Basically this code just takes some input and adds an object to the scope for that input. The table is created using an ng-repeat of this data. It's not great code at all but it's just a quick example.
The push method adds newArray to selectedLot array. It's not working on the JSON data but on arrays. If you want to have the JSON, you can give a try to :
var myJsonString = JSON.stringify(yourArray);
It will create a JSON string based on the parameter
Maybe you should try to structure your data to make lots as properties of products.
{
products: [
{id: 1, lots: [{id:1}, {id:2}]},
{id: 2, lots: [{id:1}, {id:2}]}
]
}
To add a lot to a product :
product = products[0];
product.lots.push(newArray);
Change the fallowing:
html:
<button ng-click="addLot(row.selectedProduct.id,row.newLot.value,row.newLot.id)">Add</button>
js:
$scope.addLot = function(id,val,lotId) {
// console.log(id);
var inWhichProduct = id;
var newArray = { "value": val, "id": lotId };
//console.log($scope.items)
angular.forEach($scope.items,function(v,i){
if($scope.items[i].id == id )
{
$scope.items[i].lots.push(newArray);
console.log($scope.items[i].lots);
}
});
};
http://plnkr.co/edit/W8eche8eIEUuDBsRpLse?p=preview
I'm trying to display the names of each department. I handmade a 'department' model based off of another model i made that does work. Despite them being identical, #each will not loop through the 'departments' and list them.
departments.hbs >
{{#each model}}
<tr>
<td>
{{#linkTo 'department' this}}{{this.departmentName}}{{/linkTo}}
</td>
<td>{{this.departmentName}}</td>
</tr>
{{/each}}
No errors. It just doesn't list the departments.
VpcYeoman.DepartmentsView = Ember.View.extend({
templateName: 'departments'});
VpcYeoman.DepartmentView = Ember.View.extend({
templateName: 'department'
});
VpcYeoman.DepartmentsController = Ember.ObjectController.extend({
// Implement your controller here.
});
VpcYeoman.Department = DS.Model.extend({
departmentName: DS.attr('string'),
departmentMembers: DS.attr('string')
});
VpcYeoman.Department.reopen({
// certainly I'm duplicating something that exists elsewhere...
attributes: function(){
var attrs = [];
var model = this;
Ember.$.each(Ember.A(Ember.keys(this.get('data'))), function(idx, key){
var pair = { key: key, value: model.get(key) };
attrs.push(pair);
});
return attrs;
}.property()
});
VpcYeoman.Department.FIXTURES = [
{
id: 0,
departmentName: "Sickness",
departmentMembers: "61"
},
{
id: 1,
departmentName: "Health",
departmentMembers: "69"
}
];
'department/#/' DOES work. Why is {{#each model}} not able to find the list of departments?
EDIT:
VpcYeoman.DepartmentsController = Ember.ArrayController.extend({
// Implement your controller here.
});
Upon entering {{log model}} before the {{#each model)) loop, I get this response:
[nextObject: function, firstObject: undefined, lastObject: undefined, contains: function, getEach: function…]
__ember1386699686611_meta: Meta
length: 0
__proto__: Array[0]
VpcYeoman.DepartmentsRoute = Ember.Route.extend({
renderTemplate: function() {
this.render();
}
});
VpcYeoman.DepartmentRoute = Ember.Route.extend({});
You need to declare a DepartmentsRoute with the following:
VpcYeoman.DepartmentsRoute = Ember.Route.extend({
model: function() {
return this.store.find('department');
}
});
DepartmentsController should probably be an ArrayController, and you can view the model in the console to validate it has something using ((log model)) before your each
You need to implement a model hook, returning the departments
VpcYeoman.DepartmentsRoute = Ember.Route.extend({
model: function(){
return this.store.find('department');
},
renderTemplate: function() {
this.render();
}
});
the department route is guessing based on the route name and implementing the default model hook.
I am having a problem adding an array controller as an item controller of another array controller.
Error I am getting is:
Error while loading route: TypeError {} ember.min.js:15
Uncaught TypeError: Object # has no method 'addArrayObserver'
JSFiddle: http://jsfiddle.net/t5Uyr/3/
Here is my HTML:
<script type="text/x-handlebars">
<table>
<thead>
<tr>
<th>id</th>
<th>items</th>
</tr>
</thead>
<tbody>
{{#each}}
<tr>
<td>{{id}}</td>
<td>
<ul>
{{#each items}}
<li>{{formattedName}}</li>
{{/each}}
</ul>
</td>
</tr>
{{/each}}
</tbody>
</table>
</script>
As you can see, inside the template I iterate over a collection of data with each loop, inside the each loop I want to iterate over a subcollection of the data.
Here is my JS code:
window.App = Ember.Application.create({});
App.ApplicationRoute = Ember.Route.extend({
model: function () {
var data = [
{
id: "111",
items: [
{
name: "foo"
},
{
name: "bar"
}
]
},
{
id: "222",
items: [
{
name: "hello"
},
{
name: "world"
}
]
}
];
return data;
}
});
App.ApplicationController = Ember.ArrayController.extend({
itemController: "row"
});
App.RowController = Ember.ArrayController.extend({
itemController: "item"
});
App.ItemController = Ember.ObjectController.extend({
formattedName: function () {
return "My name is " + this.get("name");
}.property("name")
});
App.RowController should be an objectController your items (rows) are objects with an array in one of their properties and not arrays themselves...
You can assing the controller in the inner each directly and remove itemController from the App.RowController.
JavaScript
App.RowController = Ember.ObjectController.extend()
Handlebars
{{each items itemController='item'}}
JsFiddle http://jsfiddle.net/mUJAa/3/