I have a directive that detects the change of a file input and aims to set a model. However the imagefile model is not being set after the file change. I have been able to print out the values of controller.modelValue[0].name and it is set. However when it returns the image file model is null.
Any suggestions on how to fix this or what am I doing incorrect?
The HTML code is here
<input type="file" id="fileInput" multiple ng-model="imagefile" file-feed/>
The directive for the file-feed attribute is
.directive('fileFeed', [
function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attributes, controller) {
element.bind("change", function(changeEvent) {
//alert("fired");
var files = [];
for (var i = 0; i < element[0].files.length; i++) {
//alert(element[0].files[i].name);
files.push(element[0].files[i]);
}
controller.$modelValue = files;
scope.$apply(function(){
**controller.$modelValue = files;**
console.log(controller.$modelValue[0].name);
});
});
}
};
}
]);
The modal controller is here
.controller('ProfilePictureModalInstanceCtrl', function ($scope, $modalInstance, items, $timeout) {
$scope.imagefile = "";
$scope.checkImageFiles = function(){
console.log($scope.imagefile);
}
$scope.ok = function () {
$modalInstance.close($scope.optionItems);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
})
I have a button in the modal that calls a function in the controller to print out the value of image file.
<button ng-click="checkImageFiles()">Check Images</button>
I am trying to return the file via the controller.modelValue property
Related
I are trying to bind a $scope.model.value to a popover.
With all the articles I have something working by storing a copy in the $scope then using a watch to update it if it changes who ever that is not a viable solution as I want to take the popover and use it multiple times with different arrays.
my html on the the main body is:
<button href="#" colorpopover
type="button"
class="btn btn-success btn-rainbow"
data-toggle="popover"
data-trigger="click"
title="Button Color"
id="static-color-popover-{{$id}}"
ng-model="model.value.buttonStatic.buttonColor">
Button Color
</button>
my controller has this code
app.directive('colorpopover', function ($compile, $templateCache, $q, $http) {
var getTemplate = function () {
var def = $q.defer();
var template = '';
template = $templateCache.get('/App_Plugins/IBD.ButtonStyles/Popovers/IBD.Color.Popover.html');
if (typeof template === "undefined") {
$http.get("/App_Plugins/IBD.ButtonStyles/Popovers/IBD.Color.Popover.html").then(function (data) {
$templateCache.put("templateId.html", data);
def.resolve(data);
});
} else {
def.resolve(template);
}
return def.promise;
}
return {
restrict: "A",
link: function (scope, element, attrs, model) {
getTemplate().then(function (popOverContent) {
// Make sure to remove any popover before hand (please confirm the method)
var compileContent = $compile(popOverContent.data)(scope);
var options = {
bindToController: true,
content: compileContent,
placement: "left",
html: true,
date: scope.date, };
$(element).popover(options);
});
}
};
});app.directive('colorpopover', function ($compile, $templateCache, $q, $http) {
var getTemplate = function () {
var def = $q.defer();
var template = '';
template = $templateCache.get('/App_Plugins/IBD.ButtonStyles/Popovers/IBD.Color.Popover.html');
if (typeof template === "undefined") {
$http.get("/App_Plugins/IBD.ButtonStyles/Popovers/IBD.Color.Popover.html").then(function (data) {
$templateCache.put("templateId.html", data);
def.resolve(data);
});
} else {
def.resolve(template);
}
return def.promise;
}
return {
restrict: "A",
link: function (scope, element, attrs, model) {
getTemplate().then(function (popOverContent) {
// Make sure to remove any popover before hand (please confirm the method)
var compileContent = $compile(popOverContent.data)(scope);
var options = {
bindToController: true,
content: compileContent,
placement: "left",
html: true,
date: scope.date, };
$(element).popover(options);
});
}
};
});
the template in basic terms
is
<div class="row">
<div ng-repeat="colorGroup in model.value" ng-class="!$last ? '' : 'last'">
<p>{{colorGroup.val1}}</p>
<p>{{colorGroup.val2}}</p>
<p>{{colorGroup.val3}}</p>
the model structure is
$scope.model.value.buttonStatic.buttonColor[val1,val2,val3]
$scope.model.value.buttonStatic.buttonHover[val1,val2,val3]
$scope.model.value.buttonStatic.buttonFocus[val1,val2,val3]
so eventually I want three buttons as a above with each of the tree values passed.
so at present the ng-repeat is repeating on the model.value of the child scope which is a direct copy of the parent.
the value in the template is going to change on the popover so it needs to go back to the parent.
With much trial and error I have solved the problem.
in the directive I needed to tie add these 2 line before the link.
require: 'ngModel', // ensures the model is passed in
scope: { model: '=ngModel' }, //ties the ng-model to the scope of the popover
then all I needed to do was set the ng-model in my button to model.value......
and in the popover template i just use model
I am using the example for ngMessages from this post:
How to add custom validation to an AngularJS form?
It works OK as long as the blacklist is a static list of items.
I would like to dynamically generate the blacklist but the directive seems to render before the blacklist is populated.
This is the directive:
.directive('blacklist', function () {
return {
require: 'ngModel',
link: function (scope, elem, attr, ngModel) {
var blacklist = attr.blacklist.split(',');
ngModel.$parsers.unshift(function (value) {
ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
return value;
});
}
};
});
This is the input where the directive is used:
<input type="text" id="DocumentName" name="DocumentName" class="form-control"
ng-model="$ctrl.document.DocumentName" ng-required="true"
blacklist="{{$ctrl.DocumentNames}}" />
In the controller when the blacklist is specified with static values it works as expected.
.component('documentDetail', {
templateUrl: '/app/document-detail/document-detail.template.html',
controller: ['Document',
function DocumentDetailController(Document) {
var self = this;
self.DocumentNames = "Install Direct Bill Invoice,Order Preacknowledgement,Order Acknowledgement"; });
When this is changed to get the DocumentNames with a service call it seems like the directive is rendered before the blacklist values are populated.
component('documentDetail', {
templateUrl: '/app/document-detail/document-detail.template.html',
controller: ['Document',
function DocumentDetailController(Document) {
var self = this;
var documentProfiles = Document.query();
documentProfiles.$promise.then(function () {
var bl = [];
for (var i = 0; i < documentProfiles.length; i++) {
bl.push(documentProfiles[i].DocumentName);
}
self.DocumentNames = bl.join(',');
});
When I inspect the element I can see the data has been populated:
But the validation acts like it is an empty string:
I tried wrapping it in a $timeout but the result was the same.
component('documentDetail', {
templateUrl: '/app/document-detail/document-detail.template.html',
controller: ['Document', '$timeout',
function DocumentDetailController(Document, $timeout) {
var self = this;
var documentProfiles = Document.query();
$timeout(function () {
documentProfiles.$promise.then(function () {
var bl = [];
for (var i = 0; i < documentProfiles.length; i++) {
bl.push(documentProfiles[i].DocumentName);
}
self.DocumentNames = bl.join(',');
});
});
How can I get these values to populate before the directive or input renders so that the blacklist can be dynamic? Thanks in advance for your help.
Use attr.$observe:
app.directive('blacklist', function () {
return {
require: 'ngModel',
link: function (scope, elem, attrs, ngModel) {
var blacklist = attrs.blacklist.split(',');
attr.$observe("blacklist",function(newValue) {
blacklist = newValue.split(',');
});
ngModel.$parsers.unshift(function (value) {
ngModel.$setValidity('blacklist', blacklist.indexOf(value) === -1);
return value;
});
}
};
});
The observer function is invoked whenever the interpolated value changes.
For more information, see AngularJS attr.$observe API Reference
Update
Using the $validators API1
The accepted answer to the referenced question uses the $parsers and $formatters pipelines to add a custom synchronous validator. AngularJS 1.3+ added a $validators API so there is no need to put validators in the $parsers and $formatters pipelines:
app.directive('blacklist', function (){
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
ngModel.$validators.blacklist = function(modelValue, viewValue) {
var blacklist = attrs.blacklist.split(',');
var value = modelValue || viewValue;
var valid = blacklist.indexOf(value) === -1;
return valid;
});
}
};
});
Notice that since the blacklist is re-computed everytime the ngModelController does a validation, there is no need to add an $observe function.
For more information, see AngularJS ngModelController API Reference - $validators.
my directive:
(function () {
var directive = function ($compile, $http, $rootScope, $translate) {
return {
restrict: 'E',
scope: {
baseUrl: '#rmsUrl',
},
link: function (scope, element, attrs) {
$rootScope.languageSwitcher = {
toggle: true,
changeLanguage: function () {
if ($rootScope.languageSwitcher.toggle) {
$translate.use('ENG');
} else {
$translate.use('FRE');
}
}
}
$rootScope.$on('oauth2:authSuccess', function (data) {
var html2 = 'French <label class="switch">'
html2 += '<input type="checkbox" ng-model="languageSwitcher.toggle" ng-change="languageSwitcher.changeLanguage()" /><div></div>'
html2 += '</label>English'
element.html(html2);
$compile(element.contents())(scope);
});
}
};
};
angular.module('testingApp')
.directive('rmsLanguageToggler', directive);
}());
my index file contain the directive:
<rms-language-toggler rms-url='blah blah blah'></rms-language-toggler>
my question:
The html render correctly, however changing/clicking the checkbox doesn't trigger the function: $rootScope.languageSwitcher.changeLanguage()
I found out why now. Turns out the ng-change and ng-model inside the directive is still referencing to the inner isolated scope. I have to use $parent. to access the outer model, i.e. $parent.languageSwitcher.toggle
I have the following markup:
<div ng-controller="DataController as vm">
<div ng-repeat="name in vm.users track by $index">
{{name}}
</div>
<form name="form" validation="vm.errors">
<input validator ng-model="vm.name" name="vm.name" placeholder="name" type="text" />
Add
</form>
</div>
I have the following controller:
function DataController($scope) {
var vm = this;
vm.name = "Mary";
vm.users = ["Alice", "Peter"];
vm.errors = 1;
vm.add = function(name) {
vm.errors++;
vm.users.push(name);
}
}
Every time I add a user I increase the value of errors.
I need to watch this variable inside a directive so I have:
app.directive("validation", validation);
function validation() {
var validation = {
controller: ["$scope", controller],
restrict: "A",
scope: {
validation: "="
}
};
return validation;
function controller($scope) {
this.errors = $scope.validation;
}
}
app.directive("validator", validator);
function validator() {
var validator = {
link: link,
replace: false,
require: "^validation",
restrict: "A"
};
return validator;
function link(scope, element, attributes, controller) {
scope.$watch(function() {
return controller.errors;
}, function () {
console.log(controller.errors);
});
}
The console.log shows the initial value but not new values:
https://jsfiddle.net/qb8o006h/2/
If I change vm.errors to an array, add the values, and watch its length then it works fine:
https://jsfiddle.net/nprx63qa/2/
Why is my first example does not work?
I update your code, you can access to the property scope.vm.errors which is updated, if you debug the code, you will see that the property controller.errors is not updated (after each digest all the watches are called to re-evaluate them). If you access the property errors from the scope you can add the $scope.$watch and make it work. However I would not recommend to have a $scope.$watch inside a directive. But that's up to you :
var app = angular.module('app', []);
app.controller("DataController", DataController);
function DataController($scope) {
var vm = this;
vm.name = "Mary";
vm.users = ["Alice", "Peter"];
vm.errors = 1;
vm.add = function(name) {
vm.errors++;
vm.users.push(name);
}
}
app.directive("validation", validation);
function validation() {
var validation = {
controller: ["$scope", controller],
restrict: "A",
scope: {
validation: "="
}
};
return validation;
function controller($scope) {
this.errors = $scope.validation;
}
}
app.directive("validator", validator);
function validator() {
var validator = {
link: link,
replace: false,
require: "^validation",
restrict: "A"
};
return validator;
function link(scope, element, attributes, controller) {
scope.$watch(function() {
return scope.vm.errors
}, function () {
console.log(scope.vm.errors);
});
}
}
https://jsfiddle.net/kcvqn5kL/
In both of your examples inside validation directive controller you assign errors property a reference to $scope.validation value.
In the first example the value is numeric and thus immutable - 1 - the reference value cannot be modified. The vm.add modifies property value of the controller instance. The change is then propagated to validation directive $scope.validation but not to the validation directive controller instance $errors property.
In the second example the value is an array and thus mutable - [] - the reference value can be modified. The vm.add does not modify property value of the controller instance. Thus the validation directive controller instance errors property value is the very same Array instance - hence it's length changes.
One way to use a immutable value (as in your first example) is to $watch a controller function as in this example:
function link(scope, element, attributes, controller) {
scope.$watch(controller.errors, function (newValue) {
console.log(newValue);
});
}
Where controller.errors is defined as follows:
function controller($scope) {
this.errors = function(){ return $scope.validation; };
}
You can find the following answer(s) useful:
Why form undefined inside ng-include when checking $pristine or $setDirty()?
i'm trying to create a layer on top of the md-chips called chip-filter
Which has some extra functionality build in;e.g.
a navigation chip (when removed, redirects the user back to a specific url)
add from any place n the app a chip
...
So I got the basics working, hooking in the componentsRegistry, and being able to call it from any place.
But now I'm trying to get the chips from the chipFilterController into the <md-chips>
html:
<chip-filter md-component-id="testId">
<md-chips ng-model="chips">
<md-chip-template>
<strong>{{$chip}}</strong>
<em>(type)</em>
</md-chip-template>
</md-chips>
</chip-filter>
and directive:
function chipFilterDirective($log) {
function postLink(scope, element, attr, sidenavCtrl) {
element.on("$destroy", function() {
sidenavCtrl.destroy();
});
}
return {
restrict: "E",
scope: {},
controller: "chipFilterController",
compile: function(element) {
return postLink;
}
};
}
and controller:
function chipFilterController($scope, $element, $attrs, $mdComponentRegistry, $q, $log) {
var self = this;
$scope.chips = [];
function addChip(input, type) {
var def = $q.defer();
$scope.chips.push({
name: input,
type: type
});
def.resolve();
return def.promise;
}
self.addNavigationChip = function() {
return addChip("stuff");
};
self.addChip = function() {
return addChip("stuff");
};
self.destroy = $mdComponentRegistry.register(self, $attrs.mdComponentId);
}
full codepen:
http://codepen.io/cskiwi/pen/eJryqK?editors=1010
setting the scope to false (or removing it from the object) made it work