Cannot access ng-if parent's scope - javascript

I have the following ng-repeat
<div ng-repeat="goal in goals">
{{ goal }}
{{ goal.bodyGoalId }}
</div>
this outputs goal and bodyGoalId as expected
with the following however
<div ng-repeat="goal in goals">
<div ng-if="goal.bodyGoalId !== null">
{{ goal.bodyGoalId }}
</div>
</div>
The if div is created and if the condition is reversed it is not created. no matter what I do to try and output the bodyGoalId however nothing is output. I imagine this has something to do with the new scope created by the ng-if but I thought the ng-if inherited its parents scope but I must not understand it correctly.
What is the correct way to get this working?
edit1:
So the above dosn't work with ng-show so it must not be a scope problem. Here is the actual code followed by the object I am trying to access. please not that %% %% is equivilant to {{ }} in this code
<table class="col-xs-12 table table-striped table-bordered table-hover">
<tr>
<th scope="col" class=""><h4>Created</h4></th>
<th scope="col" class=""><h4>Type</h4></th>
<th scope="col" class=""><h4>Targets</h4></th>
</tr>
<div ng-repeat="goal in goals">
<tr ng-show="goal.distanceGoalId !== null">
<th>%% goal.distanceGoalId %%</th>
<th>Distance</th>
<th>
Excersise: %% goal.distance_goal_desc.excersise.name %%</br>
Distance: %% goal.distance_goal_desc.distance %%</br>
Time: %% goal.distance_goal_desc.time %%
</th>
</tr>
</div>
</table>
and the %% goal %% object ouput
{
"id":"1",
"userId":"1",
"title":"first goal title",
"goalDesc":"This should describe my goal in text form",
"goalStatus":"0",
"bodyGoalId":null,
"strengthGoalId":null,
"distanceGoalId":"1",
"created_at":"2014-01-24 12:11:40",
"updated_at":"2014-01-24 12:11:40",
"body_goal_desc":null,
"distance_goal_desc":
{"id":"1","excersise":
{"id":"1","name":"Leg Extension",
"type":"Resistance",
"variation":null,
"equipment":"Machine",
"focus":"Legs",
"desc":"",
"video":null,
"created_at":"2014-01-24 12:11:41",
"updated_at":"2014-01-24 12:11:41"
},
"distance":"9.50",
"time":"01:22:00",
"created_at":"2014-01-24 12:11:40",
"updated_at":"2014-01-24 12:11:40"
},
"strength_goal_desc":null}
controller
myApp.controller('goalCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.goals = null;
$scope.getGoals = function (userid) {
$http.get('/goals/' + userid).success(function (goals, status) {
if (!isEmpty(goals)) {
$scope.goals = goals;
};
});
};
}]);
edit 2: if I remove the ng-repeat div from the table the values are displayed as normal

Related

Is there any way to make array json object display in HTML elements by using Angularjs

NOT able to display json object data on table.
JSON
{"json1":"[{\"years\":2018},{\"years\":2017},{\"years\":2016},{\"years\":2015}]",
"json2":"[{\"year5\":47.5100,\"year4\":50.9300,\"year3\":68.3700,\"year2\":65.2500,\"year1\":0.0000,
\"reportFormula\":\" Total cost of sales/total sales \",\"reportRang\":\"<35%\",\"reportTypeDetail\":\"Cost of sales % of sales\"},
let app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
let result = {
data: {
"json1": "[{\"years\":2018},{\"years\":2017},{\"years\":2016},{\"years\":2015}]",
"json2": "[{\"year5\":47.5100,\"year4\":50.9300,\"year3\":68.3700,\"year2\":65.2500,\"year1\":0.0000,\"reportFormula\":\"Total cost of sales/total sales\",\"reportRang\":\"<35%\",\"reportTypeDetail\":\"Cost of sales % of sales\"}]"
}
}
$scope.yearList = result.data.json1;
$scope.dataList = result.data.json2;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<th ng-repeat="year in yearList[0].years"> {{year}}</th>
<th ng-repeat="year in yearList[0].years"> {{year}}</th>
<tr ng-repeat="data in dataList">
<td>{{data.reportTypeDetail}}</td>
<td>{{data.reportFormula}}</td>
</tr>
</table>
</div>
I found two mayor issues with your code. First, remember the golden rule: "Always parse your JSON object".
Why I need to parse my JSON? Well, when using the JSON.parse() on a JSON derived from an array, the method will return a JavaScript array, instead of a JavaScript object.
But what this means?
It means that JSON objects is represented as a JSON string which JavaScript cannot read directly.
Ok, then how can I parse it?
The Syntax is very simple JSON.parse(text), where text is the string to parse as JSON and it will return the Object corresponding to the given JSON text.
The ng-if directive and $index attribute
Now, you are using the following code to define your <th> tags:
<th ng-repeat="year in yearList[0].years"> {{year}}</th>
Which I think it is not correct, and limits your control of the values.
I think a better solution is if you combine the ng-repeat's $index attribute with the ng-if directive. Like this:
<th ng-repeat="year in yearList" ng-if="$index == 0"> {{year.years}}</th>
This will give you more control of the values and how you want to show them. This the working code for those modifications:
let app = angular.module("myApp", []);
app.controller("myCtrl", function($scope) {
let result = {
data: {
"json1": "[{\"years\":2018},{\"years\":2017},{\"years\":2016},{\"years\":2015}]",
"json2": "[{\"year5\":47.5100,\"year4\":50.9300,\"year3\":68.3700,\"year2\":65.2500,\"year1\":0.0000,\"reportFormula\":\"Total cost of sales/total sales\",\"reportRang\":\"<35%\",\"reportTypeDetail\":\"Cost of sales % of sales\"}]"
}
}
$scope.yearList = JSON.parse(result.data.json1);
$scope.dataList = JSON.parse(result.data.json2);
console.log('yearList', $scope.yearList);
console.log('dataList', $scope.dataList);
});
table {
border-collapse: collapse;
}
table, th, td {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<th ng-repeat="year in yearList" ng-if="$index == 0"> {{year.years}}</th>
<th ng-repeat="year in yearList" ng-if="$index == 1"> {{year.years}}</th>
<tr ng-repeat="data in dataList">
<td>{{data.reportTypeDetail}}</td>
<td>{{data.reportFormula}}</td>
</tr>
</table>
</div>
Your JSON object contains two stringified json objects which JavaScript cannot directly read. To parse them use JSON.parse() like this:
JSON
{"json1":"[{\"years\":2018},{\"years\":2017},{\"years\":2016},{\"years\":2015}]",
"json2":"[{\"year5\":47.5100,\"year4\":50.9300,\"year3\":68.3700,\"year2\":65.2500,\"year1\":0.0000,
\"reportFormula\":\" Total cost of sales/total sales \",\"reportRang\":\"<35%\",\"reportTypeDetail\":\"Cost of sales % of sales\"},
JS
$scope.yearList=JSON.parse(result.data.json1);
$scope.dataList=JSON.parse(result.data.json2);
HTML
<th ng-repeat="year in yearList[0].years">{{year}}</th>
<th ng-repeat="year in yearList[1].years">{{year}}</th>
...
<tr ng-repeat="operation in dataList">
<td>{{operation.reportTypeDetail}}</td>
<td>{{operation.reportFormula}}</td>
.....
Your are using yearList[0] in your ng-repeat which returns an object like the following,
{"years":2018}
you can use
<div ng-repeat="(key, value) in yearList[0]">
{{key}},{{ value }}
</div>
to loop through your object or you can try the following to display the data from your list.
<div ng-repeat="item in yearList">{{item.years}}</div>
<br>
<div ng-repeat="operation in dataList">
{{operation.reportTypeDetail}}<br>
{{operation.reportFormula}}
</div>
Demo

Custom directive inside ng-repeat

I have this custom directive.
I call my directive like bellow, inside a ng-repeat.
selectedMealCalc.calculated_foods as 'items', is an array of objects
<!-- DIRECTIVE -->
<div ng-repeat="option in [0,1,2,3,4]">
<meal-option option="{{option}}"
items="selectedMealCalc.calculated_foods"
selectedmealcalc="selectedMealCalc"></meal-option> </div>
<!-- DIRECTIVE -->
Then I created this directive in angularjs.
'use strict';
angular.module('nutriApp').directive('mealOption', ['$compile', function($compile) {
return {
restrict: 'E',
templateUrl: 'views/checkins/meal-options.html',
scope: {
option: "#",
items: "=",
selectedmealcalc: "="
},
controller: ['$scope', 'Food', function($scope, Food) {
$scope.sumFood = {};
$scope.summerizeOption = function(foods) {
if(foods.length > 0){
$scope.sumFood = Food.summerize(foods);
}
return $scope.sumFood;
};
}]
};
}]);
And this HTML directive.
<div class="row" ng-init="filteredItems = ( items | filter: { food_option: option } )" ng-controller="CheckinsPlansCtrl">
<div class="col-md-12" ng-show="filteredItems.length > 0">
Opção {{ option }}
<table class="table table-calculo table-striped">
<thead>
<tr>
<th>Alimento</th>
<th>Quantidade</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="foodCalculation in filteredItems track by $index">
<td>{{foodCalculation.food.name}}</td>
<td>{{foodCalculation.gram_amount}} g</td>
</tr>
</tbody>
</table>
</div>
</div>
When I update the selectedMealCalc.calculated_foods the custom directive is not updating.
I have to close the modal and open again in my page to saw the new line.
As this comment Custom directive inside ng-repeat in this post.
I removed the ng-init because ng-init: "Only to initialize a property on the scope. I would recommend not to use it." as this another answer Does ng-init watch over change on instantiated property like ng-model does?

Pass object to isolated directive

When my application loads a function to get items from a server runs, this function get the title and Id of all items on the server,
then puts them in an array.
In my main html I have a table that displays this array, and when i click the title of an Item, I call another function (onView)
which gathers more information about that item from the server. This new information is passed to
a variable called '$scope.currentItem'.
I also have a directive called 'currentItem', this is used to display that item for the user.
myApp.directive('currentItem',function(){
return{
restrict:'E',
scope:{
data:'='
},
templateUrl: 'currentItem.html',
controller:function($scope){
}
};
});
To know if a user has clicked an item I have a boolean called isView, so when the function to gather more information runs
the isView is set to 'true'.
My html looks like this:
<div ng-app="myApp">
<hr>
<div ng-controller="appCtrl">
<div ng-hide="isView" class="col-md-12">
<table id="mainTable" ng-table="tableParams" class="table">
<tbody ng-repeat="item in items">
<tr>
<td id="Title" data-title="'Title'">
{{item.id}} |<a ng-click="onView(item.id)">{{item.title}}</a>
</td>
<td data-title="'Description'">
{{item.description | date:'dd-MM-yyyy'}}
</td>
<td data-title="'Created'">
{{item.created | date:'dd-MM-yyyy'}}
</td>
</tr>
</tbody>
</table>
</div>
<div ng-show="isView" class="col-md-12">
<current-item data="currentItem"></current-item>
</div>
</div>
</div>
This can't be the correct way to pass an object to a directive, seeing I always use the same 'currentItem' object.
How could I improve this, so that each object is isolated and editable in a seperate view?

AngularJS - Building a dynamic table based on a json

Given a json like this:
{
"name": "john"
"colours": [{"id": 1, "name": "green"},{"id": 2, "name": "blue"}]
}
and two regular html inputs:
<input type="text" name="name" />
<input type="text" name="color" />
<input type="submit" value="submit" />
I need to build a table with all the possible variations, ex:
John green
John blue
That means that if a user continues adding values through the inputs new rows will appear building the new variations, for instance:
I also need to have available the id to handle it, and I need that when I add new values using the inputs for instance: "Peter" "Black", I need to autofill the id (colour id) dynamically like an auto increment in mysql, resulting in something like this:
{
"colours": […...{"id": 3, "name": "black"}]
}
Is that possible? Which options do I have for doing that with angular? I'm still thinking in the jQuery way and I would like to do it in the angular way.
I took a look to hg-repeat, and used it, but I'm not figuring out how to deliver the expected result, the only thing that come to my mind was to use nested ng-repeats, but it didm´t work.
Thanks so much in advance,
Guillermo
Just want to share with what I used so far to save your time.
Here are examples of hard-coded headers and dynamic headers (in case if don't care about data structure). In both cases I wrote some simple directive: customSort
customSort
.directive("customSort", function() {
return {
restrict: 'A',
transclude: true,
scope: {
order: '=',
sort: '='
},
template :
' <a ng-click="sort_by(order)" style="color: #555555;">'+
' <span ng-transclude></span>'+
' <i ng-class="selectedCls(order)"></i>'+
'</a>',
link: function(scope) {
// change sorting order
scope.sort_by = function(newSortingOrder) {
var sort = scope.sort;
if (sort.sortingOrder == newSortingOrder){
sort.reverse = !sort.reverse;
}
sort.sortingOrder = newSortingOrder;
};
scope.selectedCls = function(column) {
if(column == scope.sort.sortingOrder){
return ('icon-chevron-' + ((scope.sort.reverse) ? 'down' : 'up'));
}
else{
return'icon-sort'
}
};
}// end link
}
});
[1st option with static headers]
I used single ng-repeat
This is a good example in Fiddle (Notice, there is no jQuery library!)
<tbody>
<tr ng-repeat="item in pagedItems[currentPage] | orderBy:sortingOrder:reverse">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.description}}</td>
<td>{{item.field3}}</td>
<td>{{item.field4}}</td>
<td>{{item.field5}}</td>
</tr>
</tbody>
[2nd option with dynamic headers]
Demo 2: Fiddle
HTML
<table class="table table-striped table-condensed table-hover">
<thead>
<tr>
<th ng-repeat="header in table_headers"
class="{{header.name}}" custom-sort order="header.name" sort="sort"
>{{ header.name }}
</th>
</tr>
</thead>
<tfoot>
<td colspan="6">
<div class="pagination pull-right">
<ul>
<li ng-class="{disabled: currentPage == 0}">
<a href ng-click="prevPage()">« Prev</a>
</li>
<li ng-repeat="n in range(pagedItems.length, currentPage, currentPage + gap) "
ng-class="{active: n == currentPage}"
ng-click="setPage()">
<a href ng-bind="n + 1">1</a>
</li>
<li ng-class="{disabled: (currentPage) == pagedItems.length - 1}">
<a href ng-click="nextPage()">Next »</a>
</li>
</ul>
</div>
</td>
</tfoot>
<pre>pagedItems.length: {{pagedItems.length|json}}</pre>
<pre>currentPage: {{currentPage|json}}</pre>
<pre>currentPage: {{sort|json}}</pre>
<tbody>
<tr ng-repeat="item in pagedItems[currentPage] | orderBy:sort.sortingOrder:sort.reverse">
<td ng-repeat="val in item" ng-bind-html-unsafe="item[table_headers[$index].name]"></td>
</tr>
</tbody>
</table>
As a side note:
The ng-bind-html-unsafe is deprecated, so I used it only for Demo (2nd example). You welcome to edit.
Here's an example of one with dynamic columns and rows with angularJS: http://plnkr.co/edit/0fsRUp?p=preview
TGrid is another option that people don't usually find in a google search. If the other grids you find don't suit your needs, you can give it a try, its free
Check out this angular-table directive.
<table class="table table-striped table-condensed table-hover">
<thead>
<tr>
<th ng-repeat="header in headers | filter:headerFilter | orderBy:headerOrder" width="{{header.width}}">{{header.label}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in users" ng-class-odd="'trOdd'" ng-class-even="'trEven'" ng-dblclick="rowDoubleClicked(user)">
<td ng-repeat="(key,val) in user | orderBy:userOrder(key)">{{val}}</td>
</tr>
</tbody>
<tfoot>
</tfoot>
</table>
refer this https://gist.github.com/ebellinger/4399082
First off all I would like to thanks #MaximShoustin.
Thanks of you I have really nice table.
I provide some small modification in $scope.range and $scope.setPage.
In this way I have now possibility to go to the last page or come back to the first page.
Also when I'm going to next or prev page the navigation is changing when $scope.gap is crossing. And the current page is not always on first position. For me it's looking more nicer.
Here is the new fiddle example:
http://jsfiddle.net/qLBRZ/3/

Using a template in a backbone view that includes the parent element

I am attempting to refactor my backbone views to have all HTML related markup in external templates. Ideally I would like to have the element that the view is attached to in the external template as well. At the moment I have:
The html template
<h3 class="pull-left">Search</h3>
<input id="customer-search-input" class="input-large search-query" placeholder="Cust #, Name, Suburb or Owner" />
<button id="customer-search-show-all" class="btn pull-right">Show All</button>
<span id="update-time" class ="pull-right"></span>
<table id="customer-search-results-table" class="tablesorter tablesorter-dropbox">
<thead>
<tr>
<th>Customer Number</th>
<th>Customer Name</th>
<th>Suburb</th>
<th>Owner</th>
<th>Phone Number</th>
</tr>
</thead>
<tbody id="customer-list-results">
</tbody>
</table>
And the backbone view that consumes the template:
define(['jquery','underscore', 'backbone', 'text!templates/customerSearch.html','text!templates/customerRow.html', 'jquery.tablesorter' ],
function($, _, Backbone, customerSearchTemplate, customerRow) {
// ... Other sub-views
var CustomerSearch = Backbone.View.extend({
id:'customer-search', // would prefer to have these
className: 'well', // in the template
initialize: function(){
this.$el.html(customerSearchTemplate);
this.customerSearchInput = this.$("#customer-search-input");
},
events: {
"click #customer-search-show-all": "showAll",
"keyup #customer-search-input": "search"
},
search: function(){
var filteredCustomers = this.collection.search(this.customerSearchInput.val(), ['id','companyName','suburb','businessContact']);
this.customerSearchResultsView = new CustomerSearchResultsView({collection: filteredCustomers});
this.customerSearchResultsView.render();
},
showAll: function() {
this.customerSearchResultsView = new CustomerSearchResultsView({collection: this.collection});
this.customerSearchResultsView.render();
}
});
return CustomerSearch;
});
Everything works but it would be great to be able to have the id and className as part of a wrapper div in the template. If I add this to the template then it appears correctly when rendered but is wrapped by another div by the backbone view.
I'm trying to decouple everything as much as possible.
Thanks!
Update 17 Oct 2012
Using the view.setElement method
var CustomerSearch = Backbone.View.extend({
template:_.template(customerSearchTemplate),
initialize: function(){
this.setElement(this.template());
},
// ...
});
with template
<div id="customer-search" class="well">
<h3 class="pull-left">Search</h3>
// ...
</div>
appears to work. Just wondering now if there is performance hit. Will report back.
You can wrap your template element within a script tag with an id.
<script id="custom-search" type="text/template">
<h3 class="pull-left">Search</h3>
<input id="customer-search-input" class="input-large search-query" placeholder="Cust #, Name, Suburb or Owner" />
<button id="customer-search-show-all" class="btn pull-right">Show All</button>
<span id="update-time" class ="pull-right"></span>
<table id="customer-search-results-table" class="tablesorter tablesorter-dropbox">
<thead>
<tr>
<th>Customer Number</th>
<th>Customer Name</th>
<th>Suburb</th>
<th>Owner</th>
<th>Phone Number</th>
</tr>
</thead>
<tbody id="customer-list-results">
</tbody>
</table>
</script>
And then, declare the following option in your view:
template : _.template($('#custom-search').html())
You will then be able to call :
this.$el.html(this.template());
in your initialize function. This will load the content of the script tag.
Why not 'wrap' your template in a parent div which includes the id / class (plus any other attribute you'd want to use)
<div id="customer-search" class="well"> ... </div>
and then use setElement to set the div as the View's el-ement.
(Note: I've never used the text plugin. But I see no reason you couldn't go with this method)
Also: more about setElement.

Categories

Resources