Simple ng-change not working properly in angularjs - javascript

I might be missing something stupidly as this simple code doesn't work as expected. What is wrong is the $scope.change function in the MainCtrl doesn't work (no alert box popped up).
In a nutshell, the view is (it's in jade, better to view?)
<body ng-app="epfApp">
...
label(ng-repeat="question in questions")
| {{ question.title }}
input(type="{{question.type}}", ng-change="change()")
and in the controller file
angular.module('epfApp')
.controller('MainCtrl', function ($scope, $window) {
$scope.questions = {
'1': {
'title': 'The first question is?',
'type': 'text',
'placeholder': 'first question'
},
'2': {
'title': 'The second question is?',
'type': 'text',
'placeholder': 'second question'
}
};
$scope.change = function() {
$window.alert('text');
};
});
And the route:
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
});
Now what it is doing properly is that it correctly populates the view with the data created (i.e. the questions json). However, What it is not doing properly is the change() function bound to the input textbox doesn't work.
What am I missing here? This obviously is a very basic job.

ng-change requires ng-model as well.
<div ng-controller="MainCtrl">
<label ng-repeat="question in questions">
{{question.title}}
<input type="{{question.type}}" ng-model="question.placeholder" ng-change="change()" />
<br />
</label>
</div>
Check out this JSFiddle.

ng-change not working but it work if you define ng-model="yourValue"... like this
<select id="editSelect" ng-model="Option" ng-change="okChange()" name="Option">
<option ng-repeat="data in Option" value="{{data.Option}}">{{data.Option}}</option>
</select>

<div ng-app ng-controller="myCtrl">
<input type="file" ng-model="image" onchange="angular.element(this).scope().uploadImage()" />
<ul>
<li ng-repeat="item in uploadcollection">
{{item.name}} ({{item.size}})
</li>
</ul>
function myCtrl($scope) {
$scope.uploadImage = function () {
$scope.uploadcollection.push({name: "Income.pdf", size: "10mb"});
$scope.$apply();
}
$scope.uploadcollection = [{name: "Income.pdf", size: "10mb"}, {name: "Expense.pdf", size: "1.5mb"}];
}
I have created a demo here: http://jsfiddle.net/fA968/120/
NOTE: This Example not worked with v1.4.8: https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js
Update
Here is an simple way to do this:
var myApp = angular.module('myApp', []);
myApp.directive('customOnChange', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var onChangeFunc = scope.$eval(attrs.customOnChange);
element.unbind('change').bind('change', function(e) {
onChangeFunc(e);
});
}
};
});
Here is an updated Fiddle

When the expression inside of the ng-change="" is something which errors (like with a spelling error) it will not log the error to the console. You will not know anything is amiss except for the fact that nothing happens.
Make sure to spell things correctly and to prefix the alias you give the "Controller As" syntax, if applicable.

Related

Strange behavior using multiple transclude component in Angularjs 1.6.1

I've a strange behavior using multiple transclude component in Angularjs:
changes in first slot model no visible in controller.
<div ng-app="myApp" ng-controller="testController">
<script type="text/ng-template" id="component-template.html">
<div style="color:red;" ng-transclude="heading">
</div>
<div style="color:blue;" ng-transclude="body">
</div>
</script>
Example1
<input ng-model="example1Model"/>
<test-component>
<panel-heading>
Example2
<input ng-model="example2Model"/>
</panel-heading>
<panel-body>
Example1Result:{{example1Model}}<br/>
Example2Result:{{example2Model}}
</panel-body>
</test-component>
</div>
<script>
angular.module("myApp", [])
.controller("testController", function ($scope, $location) {
})
.component("testComponent", {
templateUrl: "component-template.html",
transclude: {
heading: "panelHeading",
body: "panelBody"
},
controller: function ($scope, $element, $attrs) {
this.$doCheck = function () {
//do anything
}
}
});
</script>
Now if you try to test it in this JSfiddle:https://jsfiddle.net/Lpveophe/1/
Why binding model example2Model did not working?
However example1Model binding model working correctly.
You need to use . in ng-model to make it work. For better understand of scope, please go through this article: https://github.com/angular/angular.js/wiki/Understanding-Scopes
To make it work, replace example2Model with o.example2Model everywhere and change your controller to:
.controller("testController", function ($scope, $location) {
$scope.o = {};
$scope.o.example2Model = '';
})

Angular Directive not showing in ng-Switch

I have 4 templates being shown based upon var.type. I have 2/4 showing properly, but for some reasons 2 of them are not rendering anything. In the HTML I see the switch being properly activated, but the directive isn't being placed inside as expected.
angular.module('lodgicalWebApp');
angular.module('lodgicalWebApp')
.directive('lodReportVariableCgflookup', function () {
return {
template: '{{var.name}}<label for="repeatSelect"> {{var.name}} </label> \
<select name="repeatSelect" ng-model="Vars[var.name]" id="repeatSelect" ng-model="data.repeatSelect"> \
<option ng-repeat="option in var.possibleVals" value="{{option.id}}">{{option.name}}</option> \
</select>',
restrict: 'EA'
};
});
angular.module('lodgicalWebApp')
.directive('lodReportVariableBoolean', function () {
return {
template: '{{var.name}}<input ng-model="Vars[var.name]" type="checkbox" ng-true="{{var.value}}">',
restrict: 'EA',
};
});
angular.module('lodgicalWebApp')
.controller('ReportsCtrl', function ($scope, $routeParams, LodgicalReportService, Report) {
$scope.selectedReport.variables = [
{name: "Start Date",possibleVals:[],type: "Date", value: "2016-01-18"},
{name: "Operator",possibleVals: [{id:1,name:"test"},{id:2,name:"thanksStackOverflow"}], type: "CfgLookup"},
{name: "Include Security Deposits", type: "Boolean", value: "False"}
]
});
<div ng-repeat="var in selectedReport.variables">
<div ng-switch="var.type">
<!-- CfgLookup not rendering -->
<div ng-switch-when="CfgLookup" data-lod-report-variable-cfglookup ></div>
<div ng-switch-when="Boolean" data-lod-report-variable-boolean></div>
<div ng-switch-when="Date" data-lod-report-variable-date ></div>
<div ng-switch-when="CfgLookup-Multi" data-lod-report-variable-cfglookup></div>
</div>
</div>
This isn't a functioning example, but it should illustrate enough to hopefully shine some light on this major frustration.
Thanks in advance.
You have a typo that would surely mess it up:
"data-lod-report-variable-cfglookup" is what you wrote in the HTML code as the directive name, while the directive name in your JS code is "lodReportVariableCgflookup". (cfglookup vs cgflookup)
EDIT: In addition, I noticed that you used the same directive "lodReportVariableCgflookup" on both the directives that you say are not working.
It's possible you have another issue as well, but I'd start with that one :)

Angularjs bind dynamic form bulit in js

I have a form that I want to build at run time via js and use it in a form controller in angularjs.
As you can see in the following example, it is not being thrown as html, and i want it to be binded to the model variable. http://jsfiddle.net/g6m09eb7/
<div>
<form ng-controller="TodoCtrl" ng-submit="blabla()">
<div ng-repeat="field in fields">{{field.input}}</div>
</form>
</div>
function TodoCtrl($scope) {
$scope.model = {
'FirstName': 'Test',
'LastName': 'Test Last'
}
$scope.fields = [{
input: '<input type="text" ng-model="model.FirstName">'
}, {
input: '<input type="text" ng-model="model.LastName">'
}, ];
}
First, I'm going to show you how to make this work as you're trying to accomplish it, for the sake of being informative. This is not the approach you should use to solve your overall problem. This example will get the html in the document, but it won't be compiled with Angular. To do that, you would have to have a different directive, like this (click). This is all kinds of a bad approach.
angular.module('myApp', [])
.controller('TodoCtrl', function($scope) {
$scope.fields = [{
input: '<input type="text" ng-model="model.FirstName">'
}, {
input: '<input type="text" ng-model="model.LastName">'
}, ];
})
// filter to make Angular trust the html
.filter('safeHtml', ['$sce', function ($sce) {
return function (text) {
return $sce.trustAsHtml(text);
};
}])
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form ng-app="myApp" ng-controller="TodoCtrl">
<!-- use ng-bind-html on trusted html to bind it (see the js) -->
<div ng-repeat="field in fields" ng-bind-html="field.input | safeHtml"></div>
</form>
Instead, you can do this naturally. Just use the properties of your object as the criteria for ng-repeat. Simple and clean!
angular.module('myApp', [])
.controller('TodoCtrl', function($scope) {
$scope.model = {
'FirstName': 'Test',
'LastName': 'Test Last'
};
})
;
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<form ng-app="myApp" ng-controller="TodoCtrl">
<div ng-repeat="(key,value) in model">
<input type="text" ng-model="model[key]"/>
</div>
</form>
Be sure to avoid concerning your controller with DOM manipulation. If you have html snippets in a controller, your approach is probably off track. DOM manipulation should be done entirely with directives.

Is there a way to pass multiple functions to an AngularJS directive?

I am building a dropdown menu directive which allows you to optionally attach a function to each item in the list. I know how to pass one function per attribute into the directive, but I'm hoping that there is a way to pass multiple functions.
<dropdown items="['item1', 'item2']" actions="['action1()', 'action2()']"></dropdown>
or better yet:
<dropdown items="[{'item1':action1()}, {'item2':action2()}]"></dropdown>
which could be used to generate:
<dropdown items="['item1', 'item2']" actions="['action1()', 'action2()']">
<a ng-click="action1()">item1</a>
<a ng-click="action2()">item2</a>
</dropdown>
You can use the = object notation for your scope in accepting an array of objects with properties that you can assign to your directive.
DEMO
Controller
.controller('Ctrl', function($scope) {
var action = function() {
window.alert(this.label);
};
$scope.items = [{
label: 'Item 1',
action: action
}, {
label: 'Item 2',
action: action
}, {
label: 'Item 3',
action: action
}, {
label: 'Item 4',
action: action
}];
})
Directive
.directive('dropdown', function() {
return {
restrict: 'E',
scope: {
items: '='
},
template:
'<div ng-repeat="item in items track by $index" ng-click="item.action()">' +
'<a ng-bind="item.label"></a>' +
'</div>'
};
});
index.html
<body ng-controller="Ctrl">
<dropdown items="items"></dropdown>
</body>
to pass any form of function to a directive that does same thing in thesense ike a call back from the directive to execute the function , the method should be as below
First of all use the return scope to contain the functionName : '&' as it is used in passing functions
Then returning it back should look like this from ur template ng-click='functionName({params:values[,params2:value2]})'
as the above would send the param as argument to the calling controller calling the directive
var app = angular.module('testApp', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.items=['value1','value2','value3'];
$scope.items2=['value12','value22','value32'];
$scope.clicked='';
$scope.alert=function(val){
$scope.clicked=val;
}
$scope.alerti=function(val){
$scope.clicked=val+"-Second";
}
});
app.directive('direct', function(){
return {
restrict: 'E',
scope : {
actionTest:'&',
tests:'='
},
// controller: 'ctrl',
template: '<ul><li ng-repeat="test in tests" ng-click="actionTest({val:test})">{{test}} </li></ul>'
}
});
/*
app.controller('ctrl', function($scope){
});*/
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="testApp">
<div ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<p>CLicked : {{clicked}}</p>
<direct tests='items' action-test='alert(val)'></direct>
<!--with a different action function-->
<direct tests='items2' action-test='alerti(val)'></direct>
</div>
</div>

AngularJS, nested controllers from variable

Generally, what I want to do, is to initialize nested ng-controller inside ng-repeat using variable.
JSFiddle
JS
angular.module('app',[])
.controller('main',function($scope){
angular.extend($scope,{
name:'Parent Controller',
items:[
{name:'nested2'},
{name:'nested1'}
]
});
})
.controller('nested1',function($scope){
$scope.name = "Name1";
})
.controller('nested2',function($scope){
$scope.name = "Name2";
});
I want this:
<div ng-controller="main" ng-app='app'>
Nested: {{name}}
<div ng-controller="nested1">{{name}}</div>
<div ng-controller="nested2">{{name}}</div>
</div>
to become to something like this:
<div ng-controller="main">
Nested: {{name}}
<div ng-repeat="item in items">
<div ng-controller="item.name">{{name}}</div>
</div>
</div>
Problem: it does not work this way. Neither it works any other way, that I've tried after google'ing for an hour or so.
Is there any "legal" and nice way to achieve that at all?
There isn't a real way,using angular features it at this point, i suppose. You could create a directive and use un-documented dynamic controller feature controller:'#', name:'controllerName'. But this approach will not evaluate bindings or expressions that provide the controller name. What i can think of is a hack by instantiating a controller provided and setting it to the element.
Example:-
.directive('dynController', function($controller){
return {
scope:true, //create a child scope
link:function(scope, elm, attrs){
var ctrl =scope.$eval(attrs.dynController); //Get the controller
///Instantiate and set the scope
$controller(ctrl, {$scope:scope})
//Or you could so this well
//elm.data('$ngControllerController', $controller(ctrl, {$scope:scope}) );
}
}
});
And in your view:-
<div ng-controller="main">
<div ng-repeat="item in items">
Nested:
<div dyn-controller="item.name" ng-click="test()">{{name}}</div>
</div>
</div>
Demo
Note that i have changed the position of ng-controller from the element that does ng-repeat, since ng-repeat (1000) has higher priority than ng-controller (500), ng-repeat's scope will prevail and you end up not repeating anything.
While looking at it
Invested more couple hours into it, inspecting angular's sources etc.
The expression, given to ng-controller, is not being evaluated at all.
Here is best approach I've found:
HTML:
<div ng-controller="main">
Nested: {{name}}
<div ng-repeat="item in items">
<div ng-controller="nested" ng-init="init(item)">{{name}}</div>
</div>
</div>
JS:
angular.module('myApp', [])
.controller('main', function ($scope, $controller) {
angular.extend($scope, {
name: 'Parent Controller',
items: [
{name: 'nested2'},
{name: 'nested1'}
]
});
})
.controller('nested', function ($scope, $controller) {
angular.extend($scope, {
init: function (item) {
$controller(item.name, {'$scope': $scope});
}
});
})
.controller('nested1', function ($scope) {
$scope.name = 'test1';
})
.controller('nested2', function ($scope) {
$scope.name = 'test2';
});

Categories

Resources