AngularJS Nested Table - javascript

I have a table that I'm including on different pages, this works great except I can't get to the values in the included table. If I use this on a page:
<div data-ng-include="'/app/views/tasks/tasksTable.html'" />
the table shows up but I can't display the value in the file tasksTable.html, this shows undefinded:
<td>
<i class="icon-ok-sign" ng-click="addTask()"></i>
</td>
<td>
<input ng-model="task" />
</td>
From the controller:
$scope.addTask = function (data) {
console.log($scope.task);
};
If I put the table in my file instead of using ng-include to display the table I can display whatever I type into the input tied to ng-model="task".

The reason why you can't access the task is due to two reasons. Firstly, is because ng-include creates a new scope for the template that is a child of the parent controller scope. The second reason is that you are attaching your string model directly to the scope and not creating an object that contains your model. I created a working CodePen example to demonstrate how to solve your problem.
You should read up on prototypical inheritance and how it affects on scopes.
I hope this helps.

Related

AngularJs: How to communicate between two scopes

I try to follow an example from http://blog.novanet.no/creating-multilingual-support-using-angularjs/
to make multilingual support using AngularJS. This example works well.
I try another way to implement that. I use ng-include to manage module separation of the header, content and footer by using the AngularJS ng-include.
My HTML structure is like this:
<div ng-include = "'header.html'"> </ div>
<div ng-include = "'content.php'"> </ div>
<div ng-include = "'footer.html'"> </ div>
I save my work on Plunker
My question is, when I try to implement multi-language, and put the selector of language in header.html and the result on content.php it is not working.
When your templates create models, they do so on the current scope, which isn't shared with your controller scope (as mentioned in a previous answer, ng-include creates a new child scope). Because of the way inheritance works, items added to this child scope are not visible to the parent (or siblings).
However, If you add items to an item already on the parent scope (google "dot notation angular"), those additions will be visible to the parent and siblings.
A good way to do this is use the controller-as syntax. I've updated your Plunk
<body ng-app="myApp" ng-controller="myController as vm">
app.controller('myController',['$scope', 'translationService',
function ($scope, translationService){
//Run translation if selected language changes
$scope.translate = function(){
translationService.getTranslation($scope, $scope.vm.selectedLanguage);
};
//Init
$scope.vm.selectedLanguage = 'en';
$scope.translate();
}]);

Polymer repeat template bidirectional data binding

I'm trying to build a WebComponent where you can edit items in an array, with the Polymer javascript framework. Model to DOM bindings work OK, but DOM to Model doesn't - simplified example:
<polymer-element name="rep-test">
<template>
<template repeat="{{item in items}}">
<input type="text" value="{{item}}" placeholder="changes don't work!">
</template>
<button on-click="{{add}}">Add</button>
{{items}}
</template><script>
Polymer({
ready: function() { this.items = [] },
add: function() { this.items.push('') },
itemsChanged: function() { console.log(this.items) } // debug
})
</script>
</polymer-element>
<rep-test></rep-test>
The items are correctly displayed in the input elements, but when I change the value inside an input, changes are not reflected to the model (items). The binding works only in one direction.
Is there any way to make the binding bidirectional, so that when a change occur in the DOM, it is copied in the model ?
I've seen this todo demo which achieves this effect, but it does so with custom events associated with items changes. This obviously works but I'm looking for a more declarative way of doing this with bindings.
Since changes in array’s elements are not reflected to itemsChanged, I would suggest you to listen on the input changes:
<!-- ⇓⇓⇓⇓⇓⇓⇓⇓⇓ -->
<input type="text" on-change="{{ itemChanged }}"
value="{{item}}" placeholder="changes don't work!">
[...]
<!-- inside script -->
itemChanged: function(e) {
console.log(e.path[0].value)
}
Below is the link to the working example: http://plnkr.co/edit/sZYHeMuAVB0G1muHhFNK?p=preview
Here is an example of bidirectional binding: as you change the values in the input fields model is updated:
Plunk
Follow data changes:
<br>
{{testData.employees[0].firstName}}
<br>
{{testData.employees[3].firstName}}
<br><br>
<template repeat="{{person in testData.employees}}">
{{person.firstName}}
<input type="text" value="{{person.firstName}}">
<br>
</template>
I'll reference this post because it explains how this works better then I can:
"...if you change the data values, the new values are NOT available to all other instances - because the instance variables are just copies of the referenced strings. By using an object with data properties, as in the edited version above, and only ever reading from and assigning to the data properties of that object rather than overwriting the object itself, changed values are shareable between instances."

AngularJS: How do I bind data in nested ng-repeats?

I've got a pretty deeply nested JSON object being returned from an $http call, using Angular. In my template, I have to nest a few ng-repeats to get present the data. I can't seem to figure out how to bind the data using ng-model on a text input.
I read this question which said that the return object isn't automatically data in the $scope, and you have to loop through the data and instantiate the structure. I tried that as well with the same outcome.
// Seemingly unnecessary code
angular.forEach(Object.keys($scope.sources), function(sourcename){
$scope.sourceData[sourcename] = {};
angular.forEach(Object.keys($scope.sources[sourcename]), function(key){
$scope.sourceData[sourcename][key] = $scope.sources[sourcename][key];
});
Here's a fiddle showing my attempts:
http://jsfiddle.net/c7z9C/2/
I just want the values to be populated in the fields and bound to the model. Thanks in advance for any advice.
The HTML in your example was just a little off.
Here is a working fiddle.
The "not working" input just has some code in the ng-model that wasn't working.
First off, you don't need to interpolate {{ }} inside Angular directive attributes. So, this includes ng-model. So the {{key}} isn't necessary.
Also, sourceData was misspelled. It was supposed to be sourcedata and case matters.
So the end result for the ng-model is ng-model="sourcedata[key]":
<li ng-repeat="(key,value) in sourcedata">
WORKS: <input type="text" value="{{value}}" /><br/>
DOESN'T: <input type="text" ng-model="sourcedata[key]" />
</li>

Angular JS model changes not reflected in view when ng-repeat and custom directives are combined

I have the following fiddle setup to demonstrate what I am experiencing.
http://jsfiddle.net/BjornJohnson/ndxnG/25/
My controller uses these objects: page => rows => columns => pieces. A page has rows. A row has columns. A column has pieces.
There is a custom directive ("piece"). The point of the directive is to be able to provide different types of HTML elements based on the "type" of the piece (so-called dynamic templating).
I keep running into problems when I remove pieces from a column. I am modifying the model, but it's not reflected in the view. It works when the ng-repeat is on a wrapper, but not on the custom directive itself, so I think something is clashing with the ng-repeat directive.
I apologize in advance for using all caps, but I CANNOT USE A WRAPPER DIV FOR THE NG-REPEAT :)
The code in question is both in the view and the directive.
Here's part of the custom directive:
var template = templates[scope.piece.key];
var replacement = $($compile(template)(scope));
element.replaceWith(replacement);
Here's part of the view:
<div ng-repeat="column in row.data.columns" ng-class="column.data.clss">
<!-- notice wrapper for ng-repeat - THIS VIOLATES MY REQUIREMENTS -->
<div ng-repeat="piece in column.data.pieces">
<piece></piece>
</div>
</div>
versus...
<div ng-repeat="column in row.data.columns" ng-class="column.data.clss">
<!-- notice ng-repeat on custom directive -->
<piece ng-repeat="piece in column.data.pieces"></piece>
</div>

angularjs: Change parent scope from controller within a ng-switch

So, I can change a model value from a child controller, but when the child controller is in ng-switch then it doesn't work, why? I created an example to demonstrate it.
One way to avoid this is to use the . in the model name, like bunnies.kills. Is this a bug or this is a feature ?
Using Angular 1.0.6
Using your code structure, in your child controllers you would need to change:
$scope.$parent.kills++;
to
$scope.$parent.$parent.kills++;
Explanation: MainCtrl's scope is the parent scope of SimpleParentCtrl, but the grandparent of Step1Ctrl and Step2Ctrl. As some others pointed out, ng-switch creates its own scope, and then your Step1Ctrl and Step2Ctrl each created a child scope of the ng-switch.
Note: Each time the 1 or 2 button is clicked, both the ng-switch and it's currently matched child controller get a new scope.
Also: In case you happen to be looking in the Angular source and wondering how the ng-switch directive creates its own scope without a scope property, the answer is that it does so manually in its link method via scope.$new(). The directives ng-include, ng-switch, ng-repeat, and ng-view all create new scope this way, either in the link method or the compile method's returned link function.
Resources:
https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m
ng-switch creates its own child scope, which is why #sh0ber's answer is one way to get it to work. In general, models should be referenced in controller scopes (hence reference objects), and not be not primitives. So using a . is a "best practice".
This is not a bug, but it is not a feature either. This is the way JavaScript prototypal inheritance works with primitives.
I would take a slightly different approach to this problem.
Rather than use $scope.$parent, I would recommend you move all of your bunny killing logic into a shared service/model.
Also, I would try to avoid referencing parent views/controllers. Referencing the parent can make it difficult to reuse your code and can be painful to debug as the project grows. It is okay for a parent to know about it's children but a child should know little to nothing about it's parent.
Here is an updated Plunk: http://plnkr.co/edit/PLDbfU8Fu7m59A42qdR6?p=preview
HTML
<body ng-controller="MainCtrl">
<p>
Dead bunnies: <strong>{{Elmer.deadWabbits}}</strong>
</p>
<div ng-controller="SimpleParentCtrl">
<button ng-click="Elmer.killTheWabbit()">Kill from simple parent gun</button>
</div>
<hr>
<div ng-switch="" on="step">
<div ng-switch-when="first" ng-controller="Step1Ctrl">
<button ng-click="Elmer.killTheWabbit()">Kill from 1 tab gun</button>
</div>
<div ng-switch-when="second">
<div ng-controller="Step2Ctrl">
<button ng-click="Elmer.killTheWabbit()">Kill from 2 tab gun</button>
</div>
</div>
</div>
<hr>
<p>
<button ng-click="changeStep('first')">1</button> <button ng-click="changeStep('second')">2</button>
</p>
</body>
JS
angular.module('plunker', []).
service("Elmer", [function() {
this.deadWabbits = 0;
this.killTheWabbit = function() {
this.deadWabbits++;
};
}]).
controller('MainCtrl', function($scope, Elmer) {
$scope.Elmer = Elmer;
$scope.step = 'first';
$scope.changeStep = function(name){
$scope.step = name;
};
}).
controller('SimpleParentCtrl', function() {}).
controller('Step1Ctrl', function() {}).
controller('Step2Ctrl', function() {});
One way to avoid this is to use the . in model name, like bunnies.kills. Is this a bug or this is a feature ?
This has been explained numberous times : https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance
and in mhevery's video

Categories