I have a directive that consists of a select dropdown, when the select is changed (im watching the modelid) i'm recompiling the template and setting the element to the compiled html. the bizarre thing is, the span shows the correct value where as the select text is incorrect.
what i am trying to do and if anyone can provide a better solution is have part of the template dynamic so when a select is changed, i want a different part loaded in (which would also need compiling as it would have binded fields)
can anyone see why its happening please?
Thx.
app.directive('editor', ['$http', '$compile', function ($http, $compile) {
function createTemplate(id) {
var template =
'<div><select class="form-control" ng-model="lettingsourceId" >' +
'<option ng-repeat="lettingsrc in lettingSources" value="{{lettingsrc.id}}">{{lettingsrc.description}}</option>' +
'</select></div>';
return template + "<span>" + id + "</span>";
}
return {
restrict: 'E',
scope: {},
link: function ($scope, $element, $attributes) {
$scope.values= [{ id: "1", description: 'testa' }, { id: "2", description: 'testb' }];
$scope.modelid= "1";
var html = createTemplate($scope.modelid);
$element.html($compile(html)($scope));
$scope.$watch('modelid', function () {
var html = createTemplate($scope.modelid);
$element.html($compile(html)($scope));
});
}
};
}]);
checkout this
app.directive('editor', ['$http', '$compile', function ($http, $compile) {
function createTemplate(id) {
var template =
'<div><select class="form-control" ng-model="' + id + '" >' +
'<option ng-repeat="lettingsrc in lettingSources" value="{{lettingsrc.id}}">{{lettingsrc.description}}</option>' +
'</select>';
return template + "<span>{{" + id + "}}</span></div>";
}
return {
restrict: 'E',
scope: {},
link: function ($scope, $element, $attributes) {
$scope.lettingSources= [{ id: "1", description: 'testa' }, { id: "2", description: 'testb' }];
$scope.modelid= "1";
var html = createTemplate('modelid');
var element = angular.element(html);
$element.append(element);
$compile(element)($scope);
// $scope.$watch('modelid', function () {
// var html = createTemplate($scope.modelid);
// // $element.html(html);
// // $compile(elm)($scope);
// });
}
};
}]);
Related
I have a directive to build dynamic table from a collection. It have two types of columns checkbox and dropdowns. However, the scope is not resolving correctly at directive level. They are getting as 'undefined'. Also I need to bind drop down options from the controller.
html
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<ui-table source="students">
<check-col title="passed" field="passed"/>
<drop-down-col title="std" field="std" />
</ui-table>
</div>
</div>
Angular js
var myApp = angular.module('myApp',[]);
myApp.directive("uiTable", function ($compile) {
var generateTableHtml = function ($scope) {
// header
var html = "<table class='table table-condensed table-bordered table-responsive'><thead class='thead12'><tr class='active'>";
angular.forEach($scope.columns, function (col) {
debugger;
html += "<th scope='col'" +
(col.cssClass ? " class='" + col.cssClass + "'" : "") +
">" + col.title + "</th>";
});
html += "</tr></thead><tbody>";
// body
html += "<tr ng-repeat='item in dataSource'>";
angular.forEach($scope.columns, function (col) {
html += "<td" + (col.cssClass ? " class='" + col.cssClass + "'" : "") + ">";
if (col.type === ColumnType.Check) {
html += "<input type='checkbox' ng-model='item." + col.dataField + "'/>";
} else if (col.type === ColumnType.DropDown) {
html += "<select ng-model='item." +col.dataField + "' ng-options = 'option.Value for option in dataOptions'></select>";
}
html += "</td>";
});
html += "</tr>";
html += "</tbody></table>";
return html;
};
return {
restrict: "E",
replace: true,
transclude: true,
scope: {
dataSource: "=source"
},
controller: function ($scope) {
$scope.columns = [];
this.addColumn = function (col) {
$scope.columns.splice(0, 0, col);
};
},
template: "<div ng-transclude></div>",
compile: function () {
return function ($scope, $elem) {
$elem.html(generateTableHtml($scope));
$compile($elem.contents())($scope);
};
}
};
});
myApp.directive("checkCol", function () {
return {
require: "^uiTable",
restrict: "E",
replace: true,
scope: {
title: "#title",
cssClass: "#class",
dataField: "#field"
},
link: function ($scope, element, attrs, tableControl) {
$scope.type = ColumnType.Check;
tableControl.addColumn($scope);
}
};
});
myApp.directive("dropDownCol", function() {
return {
require: "^uiTable",
restrict: "E",
replace: true,
scope: {
title: "#title",
cssClass: "#class",
dataField: "#field"
},
link: function($scope, element, attrs, tableControl) {
$scope.type = ColumnType.DropDown;
tableControl.addColumn($scope);
}
};
});
//myApp.factory('myService', function() {});
var ColumnType = { Check: 1, DropDown: 2 };
myApp.controller('MyCtrl', function($scope) {
$scope.students = [{id:1, std:10, passed: true}, {id:2, std:9, passed: false}];
$scope.stds= [{Value: 10, Name: '10the std'},{Value: 9, Name: '9the std'},{Value: 8, Name: '8the std'}];
});
JSFIDDLE
Uprade the angular ver. to 1.2 and it works fine
`http://jsfiddle.net/HB7LU/18635/`
I am trying to create a page that dynamically loads a template based on the option that the user chooses from a select box. I currently have it loading the template on page load but after that it does not change based on user action.
.directive('ngUsersearch', ['$compile', '$http', '$templateCache', function($compile, $http, $templateCache) {
var getTemplate = function(contentType) {
var templateLoader,
baseUrl = 'view2/components/',
templateMap = {
beer: 'beerList.html',
brewery: 'breweryList.html',
event: 'eventList.html',
guild: 'guildList.html'
};
var templateUrl = baseUrl + templateMap[contentType];
templateLoader = $http.get(templateUrl, {cache: $templateCache});
return templateLoader;
}
var linker = function(scope, element, attrs) {
var loader = getTemplate(scope.ngModel);
var promise = loader.success(function(html) {
element.html(html);
}).then(function (response) {
element.replaceWith($compile(element.html())(scope));
});
}
return {
restrict:"E",
scope: {
ngModel: '='
},
link: linker
}
}]);
Here is my HTML:
<select ng-model="userFilter">
<option value="beer">Beer</option>
<option value="brewery">Brewery</option>
<option value="event">Event</option>
<option value="guild">Guild</option>
</select>
<ng-usersearch ng-model="userFilter"></ng-usersearch>
you forgot listen the change event of the model;
var linker = function(scope, element, attrs) {
scope.$watch('ngModel', function(newValue, oldValue) {
var loader = getTemplate(newValue);
var promise = loader.success(function(html) {
element.html(html);
}).then(function (response) {
element.replaceWith($compile(element.html())(scope)); // you compile and you have isolated scope?
});
});
}
on your compile the only scope available would be ngModel
This solution worked for me. I switched the way that the directive was loading the template. This can be done at the link function, but after the directive is set up and a part of the DOM, I was trying to remove the directive itself from the DOM by replacing it, which does not play well with how Angular's selectors work. So, now I am just replacing its contents. Also, in order to get the ng-repeat to work within the custom directive I had to add the search-results='searchResults' and then define that in the directives scope as well.
HTML:
<ng-usersearch ng-model="userFilter" search-results='searchResults'></ng-usersearch>
Controller:
.controller('View2Ctrl', [ '$scope', 'Restangular', function($scope, Restangular) {
$scope.userSearch = "";
$scope.userFilter = "beer";
$scope.search = function(userSearch, userFilter) {
$scope.searchResults = ("No " + userFilter + " Information Available");
Restangular.all('search?q=' + userSearch + '&type=' + userFilter + '&withBreweries=Y').customGET().then(function(data) {
$scope.searchResults = data;
});
};
}])
Directive:
.directive('ngUsersearch', ['$http', '$templateCache', '$compile', function($http, $templateCache, $compile) {
var getTemplate = function(contentType) {
var templateLoader,
baseUrl = 'view2/components/',
templateMap = {
all: 'all.html',
beer: 'beerList.html',
brewery: 'breweryList.html',
event: 'eventList.html',
guild: 'guildList.html'
};
var templateUrl = baseUrl + templateMap[contentType];
templateLoader = $http.get(templateUrl, {cache: $templateCache.get()});
return templateLoader;
}
var link = function(scope, element) {
scope.$watch('ngModel', function(newValue, oldValue) {
var loader = getTemplate(newValue);
var promise = loader.success(function(html) {
var rendered = $compile(html)(scope);
element.empty();
element.append(rendered); });
});
}
return {
restrict:"E",
scope: {
ngModel: '=',
searchResults: '='
},
link: link
}
}]);
I hope this helps other coders because I struggled with this for a day.
So I have this filter directive:
app.directive('filter', function(){
return {
restrict: 'E',
transclude: true,
scope: {
callFunc: '&'
},
template:
' <div>' +
' <div ng-transclude></div>' +
' </div>',
controller: function($scope, $element, $attrs){
this.getData = function() {
$scope.callFunc()
}
}
}
});
app.directive('positions', function(){
return {
require: '^filter',
scope: {
selectedPos: '='
},
template:
' Positions: {{selectedPos}}' +
' <ul>' +
' <li ng-repeat="pos in positions">' +
' {{pos.name}}</a>' +
' </li>' +
' </ul>',
controller: function($scope, $element, $attrs){
$scope.positions = [
{name: '1'},
{name: '2'},
{name: '3'},
{name: '4'},
{name: '5'}
];
$scope.selectedPos = $scope.positions[0].name;
$scope.setPosition = function(pos){
$scope.selectedPos = pos.name;
};
},
link: function(scope, element, attrs, filterCtrl) {
scope.posRegData = function() {
filterCtrl.getData();
}
}
}
})
And the controller:
app.controller('keyCtrl', ['$scope', function($scope) {
var key = this;
key.callFunc = function() {
key.value = key.selectedPos;
console.log(key.selectedPos)
}
}]);
The main question is why the key.selectedPos in the controller get's the right value only on the second click?
Here is a plunker replicating my issue.
You can send a param when you call your callFunc().
Update your func in the ctrl: key.callFunc = function(filterParams), also don't forget to update you passed method call-func="key.callFunc(filterParams)
In filter directive change your getData method to:
this.getData = function(val) {
$scope.callFunc({filterParams: val})
}
In positions directive pass the value that you need:
scope.posRegData = function() {
filterCtrl.getData({position: scope.selectedPos});
}
Now in your keyCtrl you can get the value:
key.callFunc = function(filterParams) {
key.value = filterParams.position;
console.log(filterPrams.position)
}
Here is a working plunker
As suggested, I think an isolated scope works:
app.directive('positions', function(){
return {
require: '^filter',
scope : {
selectedPos: '='
},
template:
'<button dropdown>' +
' {{selectedPos}}' +
' <ul class="dropdown-menu">' +
' <li ng-repeat="pos in positions">' +
' {{pos.name}}</a>' +
' </li>' +
' </ul>' +
'</button>',
controller: function($scope, $element, $attrs){
$scope.setPosition = function(pos){
$scope.selectedPos = pos.name;
};
$scope.positions = [
{name: '1'},
{name: '2'},
{name: '3'}
];
$scope.selectedPos = $scope.positions[0].name;
}
}
});
See:http://plnkr.co/edit/boKjmMmsCCZem0lETT3B?p=preview
You can use $broadcast on the $rootScope
in your controller do:
$rootScope.$broadcast('myEvent', $scope.selectedPos);
in your target controller you then have a listener:
$scope.$on('myEvent', function(event, myData){...})
angularJS documentation $on
and a plunker
I'm writing a directive that will lookup a value from a list of data and always display that value. Why when I click the change data button in this plunkr example does it not change the result? I've tried with a $watch but it only fires once.
Plunk: http://plnkr.co/edit/9A3z0KMm8EaBNgYv1NmH?p=preview
For example, here is the usage:
<lookup-binder lookup="lookupList" find="data.id" result="lookupList.description"></lookup-binder>
Here is what the directive looks like:
app.directive('lookupBinder', function() {
return {
restrict: 'E',
scope: {
lookup: '=',
find: '=',
result: '='
},
controller: function($scope){
console.log('controller fired');
},
link: function(scope, element, attrs, tabsCtrl) {
console.log('link fired')
scope.$watch(scope.find, function(newValue, oldValue) {
console.log('watch fired');
var el = _.find(scope.lookup, { id: scope.find });
if(el != null){
scope.result = el.description;
}
});
},
template: 'lookup Binder: <br/> lookupList: {{lookup}} <br/> find: {{find}} <br/> result: {{result}} <br/>'
};
});
Controller:
var appName = angular.module('app', []);
angular.module('app').controller('testController', TestController);
TestController.$inject = ['$scope'];
function TestController($scope) {
$scope.gradeLevels = [{
id: '1',
description: 'kindergarden'
}, {
id: '2',
description: 'first grade'
}];
$scope.gradeLevel = {
gradeLevelID: '1',
record: 'test1'
};
console.info('gradeLevels[0].id = ' + $scope.gradeLevels[0].id);
console.info('gradeLevel.gradeLevelID = ' + $scope.gradeLevel.gradeLevelID);
}
scope's $watch function takes either a string or a function, so change it to either one of them:
scope.$watch('find', function(newValue, oldValue) {
...
}
Also, the result of _.random is a number, and your ids are strings, so change the random to a string:
$scope.data.id = _.random(1,4).toString();
Check this plunker
See fiddle http://jsfiddle.net/5FdgC/2
var myModule = angular.module('myModule', [])
.controller('MyCtrl', ['$scope', function ($scope) {
$scope.model = { name: ' hans', game: 'wow' };
$scope.items = [{ name: ' jens', game: 'wow' }, { name: ' hans', game: 'wow' }];
$scope.equal = angular.equals($scope.items[1], $scope.model);
}]);
myModule.directive('selectBlock', function () {
return {
restrict: 'A',
replace: true,
template: '<select ng-model="model" ng-options="item.name for item in items"></select>',
scope: {
model: '=',
items: '='
},
link: function (scope, element, attributes) {
}
}
});
Problem:
I pass a model and some items. model angular.equals to true with an object in items. However, setting ng-model="model" in the select does not display model in the dropdown. It remains blank with the items listed beneath.
Say
items = [ob1, ob2]
angular.equals(ob2, model) = true
ng-model="model"
select renders as
--blank-- //selected value
ob1
ob2
instead of
ob1
ob2 // selected value
Cheers
Ok, I have found the problem.
http://jsfiddle.net/5FdgC/3/
.controller('MyCtrl', ['$scope', function ($scope) {
$scope.items = [{ name: ' jens', game: 'wow' }, { name: ' hans', game: 'wow' }];
// here, you have to set the default by reference, not by value.
$scope.model = $scope.items[1];
[...]