I have a JSON object like the following:
{
"sessions": [
{
"title": Session Title,
"room": "Ballroom A"
},
{
"title": Session Title #2,
"room": "Ballroom B"
}
],
"speakers": [
{
"name": John Doe,
"twitter": "jdoe"
},
{
"name": John Smith,
"twitter": "jsmith"
}
]
}
I am trying to bind to the sessions child of the object. The full object is in a variable named conferenceData and the code I am using to display the titles is:
<div *ngFor="#session of conferenceData.sessions">{{session.title}}</div>
When I do this, I get the error: TypeError:
Cannot read property 'sessions' of undefined in [conferenceData.sessions in ProductListComponent#65:17]
If I assign the child to a variable:
this.sessionData = this.conferenceData.sessions;
Then bind to the sessionData variable instead, it works as expected. This is using TypeScript and Angular 2. I suppose this could be a type issue with TypeScript, but I thought by this time it was all JavaScript. Any help would be appreciated.
Thanks!
This is probably, because this.conferenceData is not assigned yet, when Angular is rendering your view the first time.
You can easily fix this with the elvis operator:
<div *ngFor="#session of conferenceData?.sessions">{{session.title}}</div>
The problem is the time to fetch the json data, the component doesn't have the data when the angular renders the view. The solution is uses 'elvis' (?) operator to ensure the data is ready:
-
<div *ngFor="#session of conferenceData?.sessions">
{{session.title}}
</div>
The second solution is hide the ngFor until the conferenceData is ready:
<div *ngIf="conferenceData">
<div *ngFor="#session of conferenceData.sessions">
{{session.title}}
</div>
</div>
Related
I am using jquery datatable in asp.net mvc and i want to show a submit button which will be saving the data to the database only if there is atleast one row in the datatable.
I am trying this code, however its not working
<tr id="trbtnSubmit">
<td colspan="9" align="center">
<input type="submit" name="btnSubmit" value="Save"
class="btn btn-edit btn-text" />
</td>
</tr>
<script>
var PopUp, dataTable;
$(document).ready(function () {
dataTable = $("#tblCustomerList").DataTable({
"ajax": {
"url": "/Customer/GetCustomers",
"type": "GET",
"data": "json"
},
"lengthChange": false,
"pageLength": 10,
"columns": [
{ "data": "Number" },
{ "data": "Name" },
{ "data": "fileName" },
{ "data": "mD5Hash" },
{ "data": "dataSizeInGB" },
{
"data": "Id",
"render": function () {
return "<a href='#'><i class='fa fa-eye'></a></i><a href='#' style='margin-left:5px'><i class='fa fa-pencil'></i></a><a href='#' style='margin-left:5px'><i class='fa fa-trash'></a></i>";
},
"orderable": false,
"width": "40px"
},
],
"language": {
"emptyTable": "No Customers , click on <b>New Customer</b> to add Customers"
}
});
var table = $('#tblCustomerList').DataTable();
if (!table.data().any()) {
$('#trbtnSubmit').hide();
} else {
$('#trbtnSubmit').show();
}
});
</script>
Since you didn't specify the version of datatables, I assume it's v1.10.
And there are 2 side notes I want to make before going into your problem:
Difference between .datatable() and .DataTable()
Enable server-side processing
Difference Between .datatable() and .DataTable()
I saw you declared another variable, var table, at the bottom of your sample code to get another instance of DataTables and check if there is any data? You actually don't need to.
.DataTable() returns a DataTables API instance, while .datatable() returns a jQuery object.
So if you intent to make usages on the DataTables APIs after you initialize the table, you can just use the varirable you declared from the beginning, var dataTable since you used .DataTable() way.
Enable Server-side Processing
Server-side processing is enabled by turning on the serverSide option, and configuring the ajax option. You're missing the first one, whose default is false.
So you might need to add serverSide option in your code:
dataTable = $("#tblCustomerList").DataTable({
serverSide: true,
ajax: {
...
},
...
});
Enough said. Now looking at your problem ...
DataTables Callbacks
There are many ways to achieve what you want to do, and I like to use callbacks so that you can configure your DataTables in one place.
There are lots of callbacks you can use, and the one I would use is drawCallback:
dataTable = $("#tblCustomerList").DataTable({
serverSide: true,
...,
language: {
emptyTable: "No Customers , click on <b>New Customer</b> to add Customers"
},
drawCallback: function(settings) {
$('#trbtnSubmit').toggle(settings.aoData.length > 0);
}
});
Hopefully my code is readable enough without any additional explanations :)
I'm using Angular7 and ngx-translate.
i18n/en.json looks something like this:
{
"items": {
"a1": "first",
"a2": "second",
"a3": "third"
},
"nesteditems": {
"n1": {
"f1": "nested11",
"f2": "nested12",
"f3": "nested13"
},
"n2": {
"f1": "nested21",
"f2": "nested22",
"f3": "nested23"
},
"n3": {
"f1": "nested31",
"f2": "nested32",
"f3": "nested33",
"f4": "nested34"
}
}
}
Is there a way to iterate over "items" and "nesteditems" using ngFor or some other directive to display them?
You could use the keyvalue pipe in angular 7 twice if you really wanted to stick to iterating over it using ngFor.
<div *ngFor="let item of nesteditems | keyvalue">
<div *ngFor="let innerItem of item.value | keyvalue">
{{innerItem .key}}:{{innerItem .value}}
</div>
</div>
Now another recommendation would be to consider flattening the nestedItems into an array in some way, now I dont know the shapeof your data so maybe that wouldnt be reasonable though.
I am working on an rails application to manage and present images to friends and family.
In that application you can have
Events -> Subevents -> EventImages
routes.rb
resources :events do
resources :subevents do
resources :event_images
end
end
The angularjs part/page is starting when a user selects an specific event.
On the event edit page i want to present the subevents and images like that:
Subevent1 -> Upload Images Button
Image1 Image2 Image3 ...
Subevent2 -> Upload Images Button
Image1 Image2 Image3 ...
...
New Subevent Button
So basically i want to present all available subevents and the images inside each subevent on one page (there could be several subevents and several 100 images). The user should be able to add new subevents and to upload or delete images to each subevent and moving images between subevents via drag/drop. But adding/deleting/uploading/mowing is not my problem right now i just mentioned it because it might influence the solution to my problem.
Im using a nested ng-repeat to display all the information on the page
<div class="subevent" ng-repeat="subevent in event.subevents">
<li class="img" ng-repeat="image in subevent.event_images">
</li>
</div>
Im new to the angular world and im having problems now on how to retrieve the data i need for the page mentioned above.
I came up with two ideas so far:
Retrieve all the informations via an api controller in an nested form:
show.json.jbuilder
json.id #event.id
json.subevents #event.subevents do |subevent|
json.id subevent.id
json.name subevent.name
json.event_images subevent.event_images do |event_image|
json.id event_image.id
json.thumb event_image.image({ resize: "150x150" }).url
end
end
Which will give me this:
{
"id": "54d75dd9686f6c2594020000",
"subevents": [
{
"id": "54d75de1686f6c2594030000",
"name": "Test",
"event_images": [
{
"id": "54df3904686f6c41cf0c0000",
"thumb": "/uploads/event_image/54df3904686f6c41cf0c0000/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
},
{
"id": "54df56c5686f6c41cf850100",
"thumb": "/uploads/event_image/54df56c5686f6c41cf850100/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
}
]
}
]
}
And im using restangular to retrieve this information
$scope.event = Restangular.one('api/events','<%= #event.id %>').get().$object;
But this solution doesnt feel right to me. When i start to manipulate the page (uploading new images/deleting images/moving images from one subevent to another) i see myself refreshing the complete page because i see no other way here on how to just delete one image for example without reloading the complete $scope.event to get the updated page.
The other solution which came to my mind but i did not get working so far was to use the nested features of restangular to retrieve all the needed information for my page without having to create a seperate api controller.
$scope.event = Restangular.one("events",'<%= #event.id %>').all("subevents").all("event_images");
I could not find a working solution which would let me iterate over the $scope.event with ng-repeat like i did above and im not sure if this would solve my overall problem.
So the questions i would like to have answered are:
Should i stick to the API controller appreaoch or is there a way to make my second idea working to get rid of the api controller ?
How do i manipulate the images/subevents without having to reload everything ? ( an example of deleting an image on the page would be great )
This answer your second question using your current API
Plunker
app.js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.event = {
"id": "54d75dd9686f6c2594020000",
"subevents": [
{
"id": "54d75de1686f6c2594030000",
"name": "Test",
"event_images": [
{
"id": "54df3904686f6c41cf0c0000",
"thumb": "/uploads/event_image/54df3904686f6c41cf0c0000/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
},
{
"id": "54df56c5686f6c41cf850100",
"thumb": "/uploads/event_image/54df56c5686f6c41cf850100/ebd83a10e03f9794f46cda02fdbc84d3.jpg"
}
]
}
]
};
});
app.directive('myEvent', function(){
return {
scope: { myEvent: '=' },
template: "<div>{{myEvent.id}}<div my-subevent='subevent' ng-repeat='subevent in myEvent.subevents'></div></div>",
controller: function($scope){
this.removeSubevent = function(e){
$scope.myEvent.subevents.splice($scope.myEvent.subevents.indexOf(e), 1);
};
}
};
});
app.directive('mySubevent', function(){
return {
scope: {mySubevent: '='},
template: "<div>{{ mySubevent.name }} <a href='' ng-click='remove()'>remove subevent</a><div my-subevent-img='img' ng-repeat='img in mySubevent.event_images'></div></div>",
require: '^myEvent',
link: function(scope, ele, attrs, myEventCtrl){
scope.remove = function(){
myEventCtrl.removeSubevent(scope.subevent);
};
},
controller: function($scope){
this.removeImg = function(img){
$scope.mySubevent.event_images.splice($scope.mySubevent.event_images.indexOf(img), 1);
};
}
};
});
app.directive('mySubeventImg', function(){
return {
scope: { mySubeventImg: '=' },
template: "<div><img ng-src='mySubeventImg.thumb'><a href ng-click='remove()'>remove img</a></div>",
require: '^mySubevent',
link: function(scope, ele, attrs, mySubeventCtrl){
scope.remove = function(){
mySubeventCtrl.removeImg(scope.mySubeventImg);
};
}
};
});
index.html
<div my-event="event"></div>
The Problem
I have a Rails 4 backed Ember app that needs to iterate over some children, and then iterate over that child's children. The setup is Session -> hasMany -> Annotations -> hasMany -> Indicators.
I can load the /session/1 show template and display the session's properties. I can also iterate over my session's annotations and display the annotation's text. However When I iterate over the annotation's indicators, nothing shows up. If I output {{#with annotation}}{{indicators}}{{/with}} I just get <DS.PromiseArray:ember802>
Ember makes AJAX calls to /sessions/1 and /annotations?ids%5B%5D=113&ids%5B%5D=112. However it never makes a call to /indicators.
I've seen other posts that describe the same issue, but the solutions for those often came down to camel casing etc. In this case, since /indicators is never even being called, what am I doing wrong?
Environment
ember.js - 1.5.1
ember-data.js - 1.0.0-beta.7
Written in CoffeeScript
Ember Setup
Insight.ApplicationAdapter = DS.ActiveModelAdapter.extend({})
Insight.ApplicationSerializer = DS.ActiveModelSerializer.extend({})
Models
App.Indicator = DS.Model.extend {
title: DS.attr 'string'
}
App.Annotation = DS.Model.extend {
text: DS.attr 'string'
session: DS.belongsTo 'session', inverse: 'annotations'
indicators: DS.hasMany 'indicator', async: true
}
App.Session = DS.Model.extend {
subject: DS.attr 'string'
students: DS.attr 'number'
time: DS.attr 'string'
annotations: DS.hasMany 'annotation', async: true
}
Routes
App.SessionRoute = Ember.Route.extend {
model: (params)->
return #store.find('session', params.session_id)`
Session Show Template (Relevant Portion)
<section class='content'>
{{#each annotation in annotations itemController="annotation"}}
{{#with annotation}}
<li {{bind-attr class="isCompleted:completed isEditing:editing"}}>
{{#if isEditing}}
{{edit-annotation class="edit" value=bufferedText focus-out="doneEditing" insert-newline="doneEditing" escape-press="cancelEditing"}}
{{else}}
{{text}}
</div>
{{/if}}
{{#each indicator in indicators}}
<button>{{indicator.title}}</button>
{{/each}}
</li>
{{/with}}
{{/each}}
{{view Ember.TextField id="new-annotation" placeholder="Enter an annotation" valueBinding="newAnnotation" action="createAnnotation"}}
</section>
JSON GET Payloads
{"session":{
"id":4,
"subject":"Name of Subject",
"students":1,
"time":"08:52",
"annotations":[113,112]}
}
{"annotations":
[
{ "id":112,
"text":"this is my first annotation",
"session":4,
"indicators":[1]
},
{ "id":113,
"text":"This annotation has indicators",
"session":4,
"team_member":8,
"indicators":[1,2]
}
]
}
The problem lies in JSON format.
Format that you're using is the format that is accepted by the RESTAdapter
data: [
title: "Some Title",
items: [1,2]
]
ActiveModelAdapter however expects the data to be in this format:
data: [
title: "Some Title",
item_ids: [1,2]
]
To solve the problem you can either swap to RESTAdapter OR change the data producer to produce proper format instead.
JSBin with above code made working and
other question that reference the same issue.
So in my PhoneJS web app, I have a dxList widget, with checkboxes on each item. I want to be able to select multiple items, and then do something with them. I'm trying to bind the 'checked' binding to an observable, but I get an 'undefined' error.
Here's the code for the dxTemplate for the list
<div data-options="dxTemplate:{name:'item'}">
<span data-bind="text: $data.information"></span>
<div data-bind="dxCheckBox: { checked: check_boxes }"></div>
</div>
The problem is that check_boxes is in the viewModel, not the item array. I need to access values in the viewModel. I've tried viewModel.check_boxes, but with no success.
Here's the js code:
AppNamespace.otherView = function (params) {
var viewModel = {
my_list: [
{
key: 'Group 1',
items: [
{ information: 'Test 1' },
{ information: 'Test 2'},
{ information: 'Test 3' }
]
}
],
check_boxes: ko.observable(false),
//...etc
Has anyone had any experience with this, and is there a solution?
Thanks!
Knockout provides special properties to access parent binding contexts. In your case both $parent and $root should work.
More on this topic in Knockout docs: Binding context.