I have a table which I iterate throurh rows and show information for editing. There I have a file input for image uploading. File input is a bootstrap extension which provides a nice interface. Here is the link : http://plugins.krajee.com/file-input/demo
It has a simple usage you can define your input component using attributs in input tag like data-show-upload or you can use jquery to initialize the component, for example:
$("#photo").fileinput({showCaption: false});
Here is my table :
<table id="table-brand" class="table" style="border-style:none">
<tr>
<td><b>Marka Adı</b></td>
<td><b>Bilgi</b></td>
<td><b>Aktif</b></td>
<td><b>Logo</b></td>
</tr>
<tr name="brand" ng-repeat="brand in selectedCustomer.brands">
<td><input name="name" type="text" class="form-control"
id="field-brand-name"
style="width:150px" value="" title="Vergi No"/></td>
<td><textarea name="bio" type="text" class="form-control"
id="field-brand-bio"
style="width:150px" value="" title="Vergi No"></textarea></td>
<td><input index="0" id="photo0" name="photo" type="file" class="file"
accept=".jpg,.jpeg,.png" style="width:120px;"
data-show-upload="false" data-show-caption="false"
data-show-preview="false"/></td>
</tr>
</table>
The issue is when I use it with ng-repeat, the file input does not work(The javascript code bind to it doesn't work). When i removed ng-repeat then it works. I guess it's because we need to initialize those inputs using javascript like above because angular creates these elements. So tried the lines :
$(document).ready(function() {
$('[name="photo"]').fileinput({showCaption: false, showPreview: false, showUpload: false});
});
But it didn't work. Also without deleting ng-repeat, if i take input element outside '< td >' it works.
Thanks.
Edit : I debugged the line
$(document).ready(function() { .. }
and when code execution stops there, angular has not yet rendered the page so query returns nothing and no initializer executes.
I need some function that tells angular to execute after rendering the page or ng-repeat.
use this
$(document).ready(function() {
k();
});
var k= function { $('#photo0').fileinput({showCaption: false, showPreview: false, showUpload: false});}
Use a custom directive to run the jQuery code. Put the directive on the input field and run the manupilation in the link function of directive.
angular.module('moduleName')
.directive('myDirective', function () {
return {
restrict: 'EA', //E = element, A = attribute, C = class, M = comment
scope: {
//# reads the attribute value, = provides two-way binding, & works with functions
title: '#' },
template: '<div>{{ myVal }}</div>',
templateUrl: 'mytemplate.html',
controller: controllerFunction, //Embed a custom controller in the directive
link: function ($scope, element, attrs) { } //DOM manipulation
}
});
You only need the lunk function from above which gives you the directive element
Try to create on the controller a function to call the fileinput on the init of the input and pass the input id, like this
<input ng-init="myCtrl.init_input_file('your_file_input')" id="your_file_input" type="file">
And then creates the function with the fileinput on yout Js
$my.init_input_file = function(id){
$timeout(function(){
$("#"+id).fileinput();
$('#'+id).change(function(){
$my.models.files[id] = this.files;
});
});
};
try to create a place to allocate the file inputs on change before you use it.
I solve the problem with the ng-repeat with a timeout without the time, to wait the HTML render.
Related
I have a directive in AngularJS which is first initialized by default when the parent controller of it is being loaded.
The directive takes data from remote server using AJAX and implements the data in checkboxes - each checkbox get the clue if its already checked or not.
On first load of this page (controller) and directive, the checkboxes are being checked properly.
When I move to another scope (controller) and then back again to the checkboxes scope, none of them are checked.
Even though the data is being requested again from the remote server using AJAX (I console logged it), those checkboxes are ain't get checked.
Should I re-init the directive?
This is the directive (notice the checked attribute):
<checkbox key="{{num}}" selector="{{category.key}}/{{module.key}}" changefunction="togglePermission"
checked="{{permissions[num] && permissions[num].modules.indexOf(category.key+'/'+module.key)!=-1}}"></checkbox>
and this is the checkbox directive html file:
<div class="form-group" ng-class="{'has-danger': errors[selector].length>0}">
<label class="control control-outline control--checkbox {{selector}}">
<div class="input-group">
<span ng-show="title.length>0">{{title}}</span>
<input type="checkbox"
ng-model="this[key][selector]"
ng-disabled="loading"
ng-click="this[changefunction](key, selector)"
ng-checked="checked"
/>
<span class="control__indicator"></span>
</div>
</label>
<small class="text-danger" ng-show="errors[selector].length>0">{{errors[selector]}}</small>
</div>
A quick explaination: I am sending custom key and selector names, for example:
$scope.teams = {
level: ""
}
So the teams will be the key and the level will be the selector
This is the checkbox component js file:
app.directive("checkbox", ($rootScope, parseService) => {
return {
templateUrl: "/shared/checkbox/checkbox.html",
scope: true,
link: function($scope, element, attrs) {
$scope.key = parseService.parse(attrs.key);
$scope.selector = parseService.parse(attrs.selector);
$scope.changefunction = parseService.parse(attrs.changefunction);
$scope.checked = parseService.parse(attrs.checked);
}
}
});
Im binding an HTML form stored as JSON into a view template dynamically using a custom directive.
This HTML consists of a select tag and options that are generated dynamically using ng-repeat and who's model are set using ng-model.
The problem is that the bound data from the select tags, comes back as null
Here is the entire Model-View-Controller setup:
JSON with HTML:
{"location":"<input class='form-control' type='text' placeholder='City' ng-model='model.location.city'/><select class='form-control' ng-model='model.location.state'><option value=''>State</option><option ng-repeat='(k, val) in states' value='{{k}}'>{{val}}</option></select>"}
..resolves to this:
<input class='form-control' type='text' placeholder='City' ng-model='model.location.city'/>
<select class='form-control' ng-model='model.location.state'>
<option value=''>State</option>
<option ng-repeat='(k, val) in states' value='{{k}}'>{{val}}</option>
</select>
View Template:
<div bind-html-compile="html_from_json">
<!-- to be filled with HTML from JSON file -->
</div>
<button ng-click="getdata()">get form data</button>
Directive doing the binding:
.directive('bindHtmlCompile', ['$compile', function ($compile) {
return {
restrict: 'AE',
link: function (scope, element, attrs) {
scope.$watch(function () {
return scope.$eval(attrs.bindHtmlCompile);
}, function (value) {
element.html(value && value.toString());
var compileScope = scope;
if (attrs.bindHtmlScope) {
compileScope = scope.$eval(attrs.bindHtmlScope);
}
$compile(element.contents())(compileScope);
});
}
};
}]);
The Controller & Model
.controller('testCtrl', function($scope, $http, $state, $sce){
$scope.model = {
location : {
city:'', // this works fine!
state:'' // this comes back as null
}
};
$http.get('html_form.json').then(function(res){
$scope.html_from_json = $sce.trustAsHtml(res.data['location']);
$scope.getdata = function(){
console.log($scope.model);
};
});
});
This is the output to the log when "new york" is entered into the "city" field and a given state is chosen:
If I put the HTML form directly into the view (not pull it from JSON) everything works correctly. It seems that getting the data from the JSON string is what is causing this issue.
Even more strangely, the following:
console.log($scope.model.location);
does return the selected state, but expanding the object in the dev console, or trying to use the data resolves to null.
Does anyone know what may be causing this and how it can be resolved??
--UPDATE--
I found that I can get around this issue by setting the ng-models for the JSON html to non-object values. For example:
<select ng-model="state"></select> <!-- this works -->
vs.
<select ng-model="location.state"></select> <!-- this returns null -->
Then I just feed the values back to the model in my controller:
$scope.model.location.state = $scope.state;
But this is kinda crude. I would still like to know what the issue is.
Use this code below, using ng-repeat in select options is not a good practice as there is ng-options for that.
<select class='form-control' ng-model='model.location.state' ng-options='k as val for (k, val) in states'>
</select>
May be it helps you, Best of luck :)
I need to inject new template dynamically depending on the value or parameter of radio button.
This is my HTML
<div class="container-fluid" ng-app="rjApp">
<div class="panel-body" ng-controller="mainController">
<input name="penanggung_biaya" type="radio" ng-model="checked" ng-click="broadcast(umum)" class="ace" value="umum"> <!-- I though, umum is the parameter which I want passed through to the Controller -->
<input name="penanggung_biaya" type="radio" ng-model="checked" ng-click="broadcast(instansi)" class="ace" value="instansi">
<example-directive message="message"> </example-directive>
</div>
</div>
and, this is the JS
var rjApp = angular.module('rjApp',[]);
rjApp.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{::');
$interpolateProvider.endSymbol('::}');
})
//mainCotroller, should be work by ngClick through radio button
function mainController($scope, $rootScope) {
$scope.broadcast = function(event){
console.log(event) //I've been thought, this is the part to passing the parameter of radio button, but not gonna works.
$rootScope.$broadcast('handleBroadcast');
};
}
//the Directive, should be injected dinamically template, depends on ngClick parameter inside radion button
rjApp.directive('exampleDirective', function() {
return {
restrict: 'E',
scope: {
message: '='
},
link: function(scope, elm, attrs) {
scope.$on('handleBroadcast', function(doifq) {
templateUrl= '<?php echo url("registrasi/rawat_jalan/penanggung_biaya/") ?>'+doifq //This is the part to injected the dinamically template. And I've been guess the **doifq**, is the paramter to passing by the mainController
});
},
};
});
Please, somebody help me.
Regards.
In broadcast, you could pass the parameter,
$scope.broadcast = function(event){
console.log(event);
$rootScope.$broadcast('handleBroadcast',event);
};
This way you would be getting doifq value in directive depending on the which radio button is clicked.
In the following example a new field is added (by adding a blank row to $scope) when the last field loses focus if it is not empty. The problem is that the new field is not added to the DOM in time to receive focus.
Is there a way to detect when angular has finished appending new field to the DOM and then pass focus to it?
Please, no "timer" solutions; the time it takes to change DOM is unknown and I need this focus switch to happen as fast as possible. We can do better!
JSFiddle
HTML
<div ng-app='a' ng-controller='b'>
<input type="text" ng-repeat="row in rows" ng-model="row.word" ng-model-options="{'updateOn': 'blur'}">
</div>
JS
angular.module('a', []).controller('b', function ($scope) {
$scope.rows = [{'word': ''}];
$scope.$watch('rows', function (n, o) {
var last = $scope.rows[$scope.rows.length - 1];
last.word && $scope.rows.push({'word': ''});
}, true);
});
This is a View-concern and so should be dealt with by using directives.
One way to do so, is to create a directive that grabs the focus when it's linked:
.directive("focus", function(){
return {
link: function(scope, element){
element[0].focus();
}
}
});
and use it like so:
<input type="text"
ng-repeat="row in rows"
ng-model="row.word"
focus>
Demo
Use $timeout without specifying a number of milliseconds. It will, by default, run after the DOM loads, as mentioned in the answer to this question.
angular.module('a', []).controller('b', function($scope, $timeout) {
$scope.rows = [{
'word': ''
}];
$scope.addRow = function() {
$scope.rows.push({
'word': ''
});
$timeout(function() {
//DOM has finished rendering
var inputs = document.querySelectorAll('input[type="text"]');
inputs[inputs.length - 1].focus();
});
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='a' ng-controller='b'>
<div ng-repeat="row in rows">
<input type="text" ng-model="row.word" ng-model-options="{'updateOn': 'blur'}"><br>
</div>
<input type="button" ng-click="addRow()" value="Add Row">
</div>
This is part of a much more complicated directive that needs to have its own scope as well as require ngModel and replace the existing input. How can I have the directive add the ng-pattern attribute? As you can see in this jsfiddel the validation doesn't change based on the input if the ng-pattern is added in the template. This is because this will be added to an existing application that has a ton of different attributes already on a ton of different input elements, and I'm trying to make the addition as easy to implement as possible by just adding functionality to the existing input fields without messing up other things.
http://jsfiddle.net/MCq8V/
HTML
<div ng-app="demo" ng-init="" ng-controller="Demo">
<form name="myForm" ng-submit="onSubmit()">
<input lowercase type="text" ng-model="data" name="number">
Valid? {{myForm.number.$valid}}
<input type="submit" value="submit"/>
</form>
</div>
JS
var module = angular.module("demo", []);
module.directive('lowercase', function() {
return {
require: 'ngModel',
restrict: 'A',
scope:{},
replace: true,
link: function(scope, element, attr, ngModelCntrl) {
},
template: '<input class="something" ng-pattern="/^\d*$/">',
};
});
module.controller('Demo', Demo);
function Demo($scope) {
$scope.data = 'Some Value';
}
Thanks so much for any help! Ideally I would be able to just change something small and keep the ng-pattern, but I think I may have to do the validation setting on my own.
Here's how the pattern attribute is added to input item in a directive I have in my application. Note the use of compile at the end of the link function. In your case, rather than replace the element contents with a template, you'd just work with the existing element input tag.
link: function (scope, element, attrs, formController) {
// assigned template according to form field type
template = (scope.schema["enum"] !== undefined) &&
(scope.schema["enum"] !== null) ?
$templateCache.get("enumField.html") :
$templateCache.get("" + scope.schema.type + "Field.html");
element.html(template);
// update attributes - type, ng-required, ng-pattern, name
if (scope.schema.type === "number" || scope.schema.type === "integer") {
element.find("input").attr("type", "number");
}
element.find("input").attr("ng-required", scope.required);
if (scope.schema.pattern) {
element.find("input").attr("ng-pattern", "/" + scope.schema.pattern + "/");
}
element.find("input").attr("name", scope.field);
// compile template against current scope
return $compile(element.contents())(scope);
}
I tried quite a few things and it seemed that using a directive to replace an input with an input was tricking Angular up somewhere - so this is what I came up with:
http://jsfiddle.net/MCq8V/1/
HTML
<div ng-app="demo" ng-init="" ng-controller="Demo">
<form name="myForm" ng-submit="onSubmit()">
<div lowercase model="data"></div>
Valid? {{myForm.number.$valid}}
<input type="submit" value="submit"/>
</form>
</div>
JS
var module = angular.module("demo", []);
module.directive('lowercase', function() {
return {
restrict: 'A',
scope:{
data:'=model'
},
replace: true,
template: '<input class="something" ng-pattern="/^\\d*$/" name="number" ng-model="data" type="text">',
};
});
module.controller('Demo', Demo);
function Demo($scope) {
$scope.data = 'Some Value';
}
Also, you needed to escape your backslash in your regex with another backslash.