Running into a weird issue and not able to figure it out. Basically, I have a directive that manages a dropdown, and when someone chooses the "Other" option, it displays a that tracks a string value. It works perfectly fine in most browsers, but for some reason in IE11 the ng-model in the textarea element never updates in the bound controller (and subsequently doesn't update in any parent controllers either).
vm.selected (the lr-plan-picker attribute) is a javascript object with an id and body property. I've tried commenting out the hidden input elements I was using, as well as wrapped the in an ng-if separately, but that didn't work either. In the following example, I don't ever see any value in the <span ng-bind-html="vm.selected.body"></span> element. Also, the code never executes the $watch on this.selected.body, so for some reason the digest isn't triggering to let this scope know there has been changes to the DOM?
I appreciate any insight you can give in advance, I'm hoping I've missed something super obvious, because I feel like I've tried everything.
Directive/Controller:
function lrPlanPicker() {
return {
scope: {
selected: '=lrPlanPicker',
provider: '=',
disabled: '=',
name: '#',
onSelect: '&',
optedOut: '='
},
restrict: 'A',
templateUrl: require('#/src/app/referral/partials/plan-picker.tpl.html'),
controllerAs: 'vm',
controller: PlanPickerController,
bindToController: true
};
}
class PlanPickerController {
constructor($scope) {
$scope.$watch(() => {
return this.selected ? this.selected.body : null;
}, function (newVal) {
console.log(newVal);
});
this.init();
}
selectPlan(plan) {
if (plan === 'other') {
plan = {
id: 0,
body: '',
name: 'Other'
};
} else {
plan.body = plan.id;
}
this.selected = plan;
this.onSelect({ plan: plan });
}
}
Directive Template (plan-picker.tpl.html):
<div class="lr-plan-picker">
<input type="hidden"
ng-attr-name="{{vm.name}}"
ng-model="vm.selected.id"
ng-if="vm.selected.id !== 0 && !vm.optedOut"
required />
<input type="hidden"
ng-attr-name="{{vm.name}}"
ng-model="vm.selected.body"
ng-if="vm.selected.id === 0 && !vm.optedOut"
required />
<div uib-dropdown class="btn-group">
<button type="button" uib-dropdown-toggle class="btn btn-primary dropdown-toggle" ng-disabled="vm.disabled">
{{ vm.selected && vm.selected.name ? vm.selected.name : 'Select Plan' }} <span class="caret"></span>
</button>
<a lr-plan-preview="vm.selected" ng-if="vm.selected && vm.selected.id" class="plan-helper">
<i class="fa fa-question-circle-o fa-lg text-primary"></i>
</a>
<ul class="dropdown-menu">
<li ng-show="vm.dropdown.loading">
<div class="text-center"><i class="fa fa-spin fa-spinner"></i></div>
</li>
<li ng-repeat="plan in vm.dropdown.plans">
<a ng-click="vm.selectPlan(plan)">{{ plan.name }}</a>
</li>
<li ng-if="!vm.dropdown.loading">
<a ng-click="vm.selectPlan('other')">Other</a>
</li>
</ul>
</div>
<textarea ng-if="vm.selected.id === 0 && !vm.optedOut"
ng-model="vm.selected.body"
ng-disabled="vm.disabled"
required
maxlength="100"
ng-attr-name="{{vm.name}}-other"
class="form-control m-t-sm"></textarea>
<span ng-bind-html="vm.selected.body"></span>
</div>
Template using the directive (controller.tpl.html):
<div lr-plan-picker="vm.selectedPlan"
name="selectedPlan"
provider="vm.selectedProvider"
on-select="vm.onSelectPlan(plan)"
disabled="vm.isOptedOut || vm.isDontKnow"
opted-out="vm.isOptedOut">
</div>
UPDATE:
So, by process of elimination (i.e. I kept removing code until it started working), it seems like the disabled attribute is what's causing the issue. When I remove the disabled attribute from the controller.tpl.html template, the ng-model is respected. Not sure what's causing this, but when I changed the directive property from disabled to isDisabled and updated my usage of the directive, it seems to work. I don't have time to figure out why IE11 is doing something weird with directives and the disabled property, but if anyone has any insight I'd love to know. I'll mark this as Answered in a few days once it allows me to.
So, by process of elimination (i.e. I kept removing code until it started working), it seems like the disabled attribute is what's causing the issue. When I remove the disabled attribute from the controller.tpl.html template, the ng-model is respected. Not sure what's causing this, but when I changed the directive property from disabled to isDisabled and updated my usage of the directive, it seems to work. I don't have time to figure out why IE11 is doing something weird with directives and the disabled property, but if anyone has any insight I'd love to know.
Related
My app has a SLIDER and a Toggle Option, inside a Tabbed screen using AngularJS.
The problem is this. While the application and functionality work, when you load the page, when you go to another Tab, and come back, you can see both options on Browse with, and the slider disappears.
I found that if I remove the changes.paging.currentValue from the if statement, it will work. But the problem is this. What if the .length comes back undefined or null. It will throw an error.
How should I solve this?
HTML Code, in the Angular Component:
<div class="col-lg-7 col-md-7 col-sm-6 hidden-sm hidden-xs" ng-show="$ctrl.paging">
<span class="ml10 pl0">
<a class="pull-left f10 m10" ng-repeat="item in $ctrl.paging track by $index" ng-hide='item.type===$ctrl.parameters.type'
ng-click="$ctrl.changeBrowsingType(item, $ctrl.parameters.partition.id)">Browse with {{item.label}}</a>
<rzslider ng-if="$ctrl.parameters.type === $ctrl.browsingType.OPTION1" rz-slider-model="$ctrl.parameters.start"
rz-slider-options="$ctrl.slider.options" class="custom-slider"></rzslider>
<rzslider ng-if="$ctrl.parameters.type === $ctrl.browsingType.OPTION2" rz-slider-model="$ctrl.timestampStart"
rz-slider-options="$ctrl.slider.options" class="custom-slider"></rzslider>
</span>
</div>
The problematic function in the controller:
const onChangesFn = (changes) => {
if (changes.paging.currentValue && changes.paging.currentValue.length) {
ctrl.paging = changes.paging.currentValue;
**...more code functionality**
};
**....more code here**
}
};
Thanks for your time.
I have found an issue in AngularJS which relates to wrong update of view. It occurs from time to time. The problem is when model gets a new value, view is not updated by new model value, but old value is appended by new model value.
While troubleshooting I checked that model contains a correct value.
Here is a view.
<div class="container">
<div ng-repeat="p in point" id="{{'point-' + p.Id}}" class="{{p.BackgroundClass}}">
<div class="point-number">{{p.Id}}</div>
<div class="{{p.ImageClass}}"></div>
<div class="point-amount">{{p.Amount}}</div>
<div class="point-quantity">{{p.Quantity}}</div>
</div>
</div>
Controller code which contains SignalR events processing:
wetApiHubProxy.on('updatePointState', function (pointId, backgroundClassProp, imageClassProp) {
pointsService.getPointById(pointId).then(function (point) {
point.BackgroundClass = backgroundClassProp;
console.log('imageClassProp ' + point.ImageClass);
point.ImageClass = imageClassProp;
});
});
p.ImageClass is changing quite often. Changes/updates of view work in a correct way until sometimes occurs concatenation of old and new value.
Old p.ImageClass value is "point-state-configure".
New p.ImageClass value is "pump-state-off".
As a wrong result I have, where ImageClass contains concatenated values:
<div ng-repeat="p in points" id="point-4" class="point point-off" role="button" tabindex="0" style="">
<div class="point-number ng-binding">4</div>
<div class="point-state-configure pump-state-off" style=""></div>
<div class="point-amount ng-binding">926.93</div>
<div class="point-quantity ng-binding">417.35 L</div>
</div>
I have tried to call $scope.$apply() and $evalAsync, but that was hopeless. The strangest thing that issue occurs spontaneously. The only constant condition it's when $rootscope contains bigger amount of child scopes. Can anyone tell what place to dig and how to get rid of this problem?
class attribute is not intended to be used this way. You should use the ng-class directive instead.
I've created an example for you: https://jsfiddle.net/coldcue/o7q6gfs4/
JavaScript
angular.module('testApp', [])
.controller("TestController", function($scope) {
// Initialize the value
$scope.state = "state-blue";
// Change class on click
$scope.click = function() {
$scope.state = ($scope.state === "state-blue") ? "state-red" : "state-blue";
}
});
HTML
<div ng-controller="TestController">
<div ng-class="state">
Some label
</div>
<input type="button" ng-click="click()" value="Click me">
</div>
But there are many more ways to use ng-class, read more here: https://docs.angularjs.org/api/ng/directive/ngClass
I want to create a directive which allow me to generate validation message near input - based on ngMessages(as in example).I have this working HTML example:
<div class="field">
<div class="ui right icon input">
<input type="email" name="email" ng-model="vm.user.email" placeholder="E-mail" required>
<i class="at icon"></i>
</div>
<div ng-messages="vm.signUpForm.email.$error" ng-show="vm.signUpForm.$submitted">
<div ng-messages-include="shared/validation/formErrorMessages.html"> </div>
</div>
</div>
My current directive:
var app = angular.module('app.directives', []);
app.directive('formError', function() {
return {
restrict: 'AE',
replace: 'false',
scope: {
statement: '#',
error: '#'
},
template: '<div ng-messages="error" ng-show="true"><div ng-messages-include="shared/validation/formErrorMessages.html"></div></div>'
};
});
And how I tried to run it:
<div form-error error="{{ vm.signUpForm.email.$error }}" statement="{{ vm.signUpForm.$submitted }}"></div>
It's not working - message won't appear - without any error. On message show I will also want to add class 'error' to 'div.field', but it should be easy.
Any idea how to make this directive work or maybe how to handle this in another, more comfortable way?
You made a a mistake, you should pass attributes to directive with
scope: {
statement: '=',
error: '='
},
# biding is for passing string values, not objects and error is an object, so passing it that way will not work as expected. Of course you could use attr.$observe and JSON.parse, but that is not what you wanna do here.
https://plnkr.co/edit/iRRPqLpmqdQltNjw35Nb?p=preview
im new to angular and was struck linking dropdown selected to ng-click button
<div class="col-xs-2">
<select name="cars" ng-model="dropdown_data">
<option>email</option>
<option>phone</option>
<option>username</option>
</select>
</div>
<br />
<div class="col-xs-4">
<button type="button" class=" " data-ng-click="search_{{dropdown_data}}()">Search</button>
</div>
<script>
var ng = angular.module('myApp', []);
ng.controller('ctrl', function($scope, $http) {
$scope.search_phone = function() {
alert("phone")
}
$scope.search_email = function() {
alert("email")
}
})
</script>
this seems to be fairly simple but im not sure what im doing wrong...Im not able to show alerts depending on selected dropdown
Plunker link http://plnkr.co/edit/Iicm9tvfizXxNl3MwtZI?p=preview
any help is much appriciated...thanks in advance
There were few things that you needed in the plunkr.
Firstly you need to define on the HTML that it is in fact an Angular Application (via the ngApp attribute).
Secondly you need to define a controller for your view (via the ngController attribute).
Once you have those things in place, you need to understand what this would do
ng-click="search_{{dropdown_data}}()"
If you think about how ng-click works, it registers a function on click. This happens on the compile phase of a directive (as you can see on its sourcecode).
This means that when the directive compiles, it will register the function with the name search_{{dropdown_data}} and even though the dropdown_data will be interpolated later on when its value changes, the originally bound function won't update.
However if you had dropdown_data as an attribute or as a key to a map of functions that will work. Here an example of how you may do that:
$scope.search = {
phone: function() {
alert("phone")
},
email: function() {
alert("email")
}
};
and on the button: data-ng-click="search[dropdown_data]()"
Here a working plunkr: http://plnkr.co/edit/u4vJj2a0r1a95w64crHM?p=preview
I am also new in angular but have used same functionality without search button direct given anchor link try this if you need,
<div ng-app="myApp">
<div ng-controller="setContent">
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Subject
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li ng-repeat="a in subjects">{{a}}</li>
</ul>
</div>
var app = angular.module('myApp', []);
app.controller('setContent', function($scope, $http) {
$scope.subjects = ['Math', 'Physics', 'Chemistry', 'Hindi', 'English'];
});
});
The problem is this data-ng-click="search_{{dropdown_data}}()". Better to pass a value to the function like this:
<button type="button" data-ng-click="search(dropdown_data)">Search</button>
$scope.search = function(type) {
alert(type)
}
also dont forget ng-app and ng-controller.
see the plunker
I am new to angularJs. I am trying to create new directive which contains input element and a button. I want to use this directive to clear input text when button is clicked.
When I use my directive in html I am getting below error :
Error: [$compile:tplrt] Template for directive 'cwClearableInput' must have exactly one root element.
html:
<div class="input-group">
<cw-clearable-input ng-model="attributeName"></cw-clearable-input>
</div>
clearable_input.js:
angular.module('cw-ui').directive('cwClearableInput', function() {
return {
restrict: 'EAC',
require: 'ngModel',
transclude: true,
replace: true,
template: '<input type="text" class="form-control"/><span class="input-group-btn"><button type="button" class="btn" ng-click="" title="Edit"><span class="glyphicon-pencil"></span></button></span>',
controller: function( $scope ) {
}
};
});
I am not able to figure it out how to achieve this.
Well, the error is pretty self-explanatory. Your template needs to have a single root and yours has two. The simplest way to resolve this would be to just wrap the whole thing in a div or a span:
template: '<div><input type="text" class="form-control"/><span class="input-group-btn"><button type="button" class="btn" ng-click="" title="Edit"><span class="glyphicon-pencil"></span></button></span></div>',
Before:
<input type="text" class="form-control"/>
<span class="input-group-btn">
<button type="button" class="btn" ng-click="" title="Edit">
<span class="glyphicon-pencil"></span>
</button>
</span>
After:
<div> <!-- <- one root -->
<input type="text" class="form-control"/>
<span class="input-group-btn">
<button type="button" class="btn" ng-click="" title="Edit">
<span class="glyphicon-pencil"></span>
</button>
</span>
</div>
This error will also occur if the path to the template is incorrect, in which case the error is everything but explanatory.
I was referring the template from within templates/my-parent-template.html with the (incorrect)
template-url="subfolder/my-child-template.html"
I changed this to
template-url="templates/subfolder/my-child-template.html"
which solved it.
Just wrap your template in something:
template: '<div><input type="text" class="form-control"/><span class="input-group-btn"><button type="button" class="btn" ng-click="" title="Edit"><span class="glyphicon-pencil"></span></button></span></div>',
If you don't want to make one root element you can instead set replace to false. In your directive you are setting it to true, which replaces the directive tag "cw-clearable-input" with the root element of your directive.
If you have a comment in your template alongside the root element in the directive template, you will see the same error.
Eg. If your directive template has HTML like this (with "replace" set to true in the directive JS), you will see the same error
<!-- Some Comment -->
<div>
<p>Hello</p>
</div>
So to resolve this in scenarios where you want to retain the comments you will need to move the comment so that it is inside the template root element.
<div>
<!-- Some Comment -->
<p>Hello</p>
</div>
When none of this worked for me, prefixing a / to the template path fixed the issue in my case.
function bsLoginModal() {
return {
restrict: 'A',
templateUrl:'/app/directives/bootstrap-login-modal/template.html',
link: linkFunction,
controller: SignInCtrl,
controllerAs: 'vm'
}
}
instead of
templateUrl:'app/directives/bootstrap-login-modal/template.html
Good Luck.
Check your template or templateUrl
templateUrl: 'some/url/here.html'
template: '<div>HTML directly goes here</div>'
My problem was that webpack's HtmlWebpackPlugin was appending a script tag to the directive content. So angular was seeing:
<div>stuff</div>
<script type="text/javascript" src="directives/my-directive"></script>
The solution is to use HtmlWebpackPlugin's inject option in your webpack.config.js:
new HtmlWebpackPlugin({
template: './src/my-directive.html',
filename: './src/my-directive.html',
inject: false
}),