I don't understand how this is possible. Please clear this up for me. I am reading about the scope inheritance and, as far as I understood, we cannot access child's scope from the parent, right?
So I have this situation where I created a controller but forgot to set it in the template via ng-controller detective. But it still works. I never noticed it. So is this the expected behaviour or I am just missing something? I din't even know what parts of code to list here.
It is an ordinary controller
angular.module('Basal.tables')
.controller('ListTablesCtrl', function($scope, getTablesS) {
$scope.tables = {};
getTablesS.fetch(function (d){
$scope.tables = d;
});
});
... executed at location change
when('/tables', {
templateUrl: '/views/tables/list-tables.html',
controller: 'ListTablesCtrl'
}).
But there is no mention of the controller in the template.
<div class='frame' id='list-tables'>
<section class='title'> Tables </section>
<table>
<tr ng-repeat='table in tables'>
<td><input type='checkbox'></td>
<td>{{ table.title }}</td>
<td>{{ table.desc }}</td>
</tr>
</table>
</div>
Everything works though. How is it possible to see the tables object here?
There is the top Main Controller set on the html body which means that it operates under this main controller in the template. But then how does it accesses child's scope?
Please explain if I am missing something silly.
Thanks.
Angular looks upwards for a controllers/method.
Meaning if it is not in the current scope, it will look into the parent scope.
But in your case, you have have attached the controller in your route file.
when('/tables', {
templateUrl: '/views/tables/list-tables.html',
controller: 'ListTablesCtrl'
})
Related
I'm creating HTML code inside javascript code, the problem is it doesn't recognize that it's an Angularjs code, how can I do that please ?
my problem is that the variables in {{}} are not recognized as Angularjs code and are put like that when I call the function from the view, even though on top of the view I have the declaration of ng-app and ng-controller on tope of the view.
Any help please ?
You have to inject ng-sanitize into your app and then include the ng-bind-html directive in your html in the elements you're generating from your controller.
So where you create your app module do something like:
angular.module('myApp',[ngSanitize])
That being said, you're doing it wrong. :)
Define the table in your html and use ng-repeat to generate the rows. I'm guessing there's something else to this, but it looks like you're trying to generate a table dynamically after some event occurs. Just put the table in your html and use ng-if to hide it until the event occurs.
Or do it in a component.
Your component html would basically just be your table layout like you're generating in the factory code stored in tableauComponent.html.
<table>
<tr>
<th>Matricule</th>
<th>Sexe</th>
<th>Direction</th>
<th>Type_contrat</th>
</tr>
<tr ng-repeat="x in tableau.data">
<td>{{ x.MATRICULE }}</td>
<td>{{ x.SEXE }}</td>
<td>{{ x.DIRECTION }}</td>
<td>{{ x.TYPE_CONTRAT }}</td>
</tr>
</table>
The component would get registered with your app with something like this:
angular.module("myApp").component("tableauComponent", {
templateUrl: 'tableauComponent.html',
controller: tableauController,
controllerAs: 'tableau'
})
function tableauController() {
var ctrl = this;
this.data = service call to get your data here.
}
Then whereever you want this baby to show in your html you just use:
<tableau-component></tableau-component>
I have a table that I'm populating from a database using ng-repeat to produce objects that will be capable of being dragged and dropped. When I drag and drop an object onto a cell I need a way to find the id that comes from the initial load from the database so that I can update the database with the new position of the object within the table. From my understanding you should be able to reference the variable in the ng-repeat expression to do this? For example:
you could reference the x in: x in names? This is just example code of a table using ng-repeat:
<div ng-app="myApp" ng-controller="customersCtrl">
<table>
<tr ng-repeat="x in names">
<td>{{ x.Name }}</td>
<td>{{ x.Country }}</td>
</tr>
</table>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('customersCtrl', function($scope, $http) {
$http.get("http://www.w3schools.com/angular/customers.php")
.then(function (response) {$scope.names = response.data.records;});
});
</script>
I have tried to find an answer for this, by trying to just use Javascript to reference elements produced by angular, but that doesn't seem to work as they are not written to the html source (unless I inspect elements, but that doesn't show my id's). I also thought I could just reference this from the scope, but it would appear each instance of x would get its own scope variable. Is there any way to do something like this within angular context or would you need to use something like jquery?
I have the following code (see below) in whichI use ng-include. The ng-model="numLines" creates a binding of the value in the box and the function changeNumLines() specified in ng-change is called every time the value inside the input box changes. The value of the $scope.numLines inside the changeNumLines() is supposed to be the new changed value. However, it is not. The value that I output to the console is always "1" no matter what input is typed in the box.
However, if I do not use ng-include and just copy paste the partial html file: structCustomRows.htm into index.html instead of ng-include line everything works fine.
So, why is this happening with ng-include and how can I get around it?
Thanks.
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<base href="/">
</head>
<script>
var app = angular.module('parserUI', []);
app.controller('CustomRowsCtrl', ['$scope', function($scope) {
$scope.numLines = 1;
$scope.changeNumLines = function() {
console.log("DEBUG: changeNumLines()");
console.log(String($scope.numLines));
}
}]);
</script>
<body ng-app="parserUI">
<h2>Header</h2>
<div ng-controller="CustomRowsCtrl">
<div ng-include src="'app/partials/structCustomRows.htm'"></div>
</div> <!-- ng-controller="CustomRowsCtrl" -->
</body>
</html>
structCustomRows.htm
<input type="text" ng-model="numLines" ng-change="changeNumLines()">
Looking at the docs, it says that "This directive creates new scope". Meaning the $scope.numLines in your controller is actually in the parent scope of your ngInclude directive.
The simplest way to fix this is to put the controller definition in the ngInclude to make it all the same scope. Otherwise, you will have to access the parent scope using something like $scope.$parent.numLines.
When you use ng-include, Angular creates a new scope, causing your variable to be overwritten to the new scope, instead of the one you wanted. As the new scope is an instance of the original scope (the one you want), you can create an object on CustomRowsCtrl; the objects are copied to the new scope as a reference, enabling you to share the same object in between child and parent scope.
Obs.: You can also use $parent, as suggested on the link below, but using pointers is a lot cleaner and if you use multiple nesting, you won't need to use $parent.$parent and so on.
Example:
On the controller:
$scope.selection = {}
On the HTML:
ng-model="selection.numLines"
More about ng-include scope:
AngularJS - losing scope when using ng-include
It's all about scope creation in Angular.
The directive ng-include create a new scope and you've defined the numLines in the parent scope, i.e the scope of the controller.
If you want to see the changes in your parent scope, you'll have either to write ng-model="$parent.numLines" in the included Html template or simply use the onload attribute of the ng-include directive : onload="numLines = numLines"
ng-include directive creates its new scope, There is so many work round for tackle this problem.
Create your own directive of static include which does not creates its own scope. for example.
app.directive('staticInclude', ['$http', '$templateCache', '$compile', function ($http, $templateCache, $compile) {
return function (scope, element, attrs) {
var templatePath = attrs.staticInclude;
$http.get(templatePath, { cache: $templateCache }).success(function (response) {
var contents = $('<div/>').html(response).contents();
element.html(contents);
$compile(contents)(scope);
});
};
}]);
so you can use this directive, its not create own scope.
Your html looks like
<body ng-app="parserUI">
<h2>Header</h2>
<div ng-controller="CustomRowsCtrl">
<div static-include=="app/partials/structCustomRows.htm"></div>
</div> <!-- ng-controller="CustomRowsCtrl" -->
</body>
You include controller in template means your file 'app/partials/structCustomRows.htm' has main div which initialize its relevant controller.
I'm under the impression that ng-repeat creates a new scope for each element in the array/object.
Is it possible to access these new scopes that the ng-repeat creates from the controller? For example if you know the index?
Any help would be much appreciated.
Check the console of this demo: JSFiddle.
console.log the scope, there are two attributes $$childHead and $$childTail. They are the first and last child scopes created by ng-repeat.
After getting the first child scope $$childHead, you can traverse to get other ng-repeat scope objects through $$nextSibling and $$prevSibling.
Note: these attributes start with $$, which indicate that they are private Angular variables. I suppose they are not intended to be used directly.
If you use ng-repeat like <div ng-repeat="item in items"></div>, you can use ng-click="dealWithItem(item, $index)" to pass this item to a function defined in the controller:
$scope.dealWithItem = function (item, index) {
// console.log(item);
}
It works for nested ng-repeat as well: JSFiddle.
When I tried Joy's answer, I got undefined for the item variable at the console. But there is an easier way to do it.
html:
<tr ng-repeat="name in names">
<button class="btn btn-info btn-sm pull-right" ng-click="dealWithItem()">{{name.first_name}}</button>
</tr>
Controller:
$scope.dealWithItem = function () {
console.log(this.name.first_name);
}
this.name will have all of the attributes associated with $scope.names.
You can use $parent as an argument of your function to access these scopes:
<div ng-repeat="item in items">
<div ng-click="doSomethingWithItem(item, $parent)"></div>
</div>
I am using angularjs and the angular ui router to load nested templates. The child's controller inherits the parent's $scope for it's contents. This is all working fine except when I try to load the child scope as the initial view, the child scope is empty.
www.domain.com/products - parent view - works
www.domain.com/products/product-child view - works when navigating from parent.
When I try to load www.domain.com/products/product directly, the child template appears to load before the parent is complete leaving the child scope empty.
Here is the ui router info.
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state('products', {
url: "/products",
templateUrl: "/views/products.html",
controller: "ProductsCtrl"
})
.state('products.product', {
url: "/:description",
templateUrl: "/views/product.html",
controller: "ProductCtrl"
})
});
What am I missing?
Parent View:
<section class="product-container">
<a ui-sref="products.product({description:product.description})" ng-repeat="product in products | orderBy : orderObjectBy(products,'active',true)" class="product" ng-click="" ng- class="{inactive:!filterProducts(product, products.keywords) && isFilterChecked() , 'selected- product':(product.selected)}" ng-style="{left: ((product.pos_left*360) + 'px'),top: ((product.pos_top*360) + 'px')}">
<img ng-src="http://www.tiempotimepiece.com/catalog/{{product.SmallImageUrl}}">
<p>{{product.description}}</p>
{{product.price | currency}}
<p>{{product.order}}</p>
</a>
<div ui-view></div>
</section>
Child View:
<div ng-repeat="product in items | filter: {description:description}">
Sku:{{product.Sku}}<br>
IdProduct:{{product.IdProduct}}<br>
price:{{product.price}}<br>
description:{{product.description}}<br>
SmallImageUrl:{{product.SmallImageUrl}}<br>
</div>
The child controller inherits the parent $scope by using $scope.$parent. When loading the child view as the initial page, the child view/controller appears to load before the parent has completed populating the scope.
What am I missing here? Thanks!
UPDATE: I do think a service is the quickest fix here and probably the best solution, but I am adding links to the Gists of the two controllers if anybody is interested in looking into this further to see if that is the best approach. These are a work in progress and have not been refactired at all and are frankly a mess at this point. There is a lot of crazy filtering and sorting going on.
Products (Parent) : https://gist.github.com/bennewton999/8257080
Product (Child) : https://gist.github.com/bennewton999/8257058
Ok, so I came in Monday morning with a fresh mind and realized I didn't need a different controller for this view at all. I just changed the controller to use the products controller for the product view and that was it.