I have shop attribute, which I want to treat differently, depending on what type passed into (it can be string or object). How do I properly bind this attribute, so that I could pass string and object, and then treat it differently, depending on the passed type?
This way object passing works perfectly fine, but passing a string gives this attribute a value '0':
angular.module('showcaseApp')
.directive('card', function ($window, $state) {
return {
templateUrl: '/card.html',
restrict: 'E',
replace: true,
scope: {
shop: '='
},
link: function(scope, element, attrs) {
attrs.$observe('shop', function (value) {
if (value) {
}
});
}
};
});
Binding with & allows me to pass a string (catch it with $observe), but an object can't be passed this way. And I want to be able to pass both. This can be solved only by creating a new attribute with different binding?
Figured it out. I passed a string like this:
var id = scope.pageContent.content[i].id;
Instead I should pass it like this:
var id = "'" + scope.pageContent.content[i].id + "'";
And everything works ✨
Related
In html I have this object passed into directive
<lcd-code ldcCode="{{ detail.program.ldcCode }}"></lcd-code>
detail.program.ldcCode = "PSIH" ...
However in the Directive it is undefined
var lcdCode = function (customerService, $sce) {
return {
replace: true,
restrict: "E",
scope: {
ldcCode: "=" // two way
},
link: function (scope, element, attrs) {
console.log('scope.ldcCode',scope.ldcCode); // says undefined
}
};
}
Previously I was using "#" and then attrs.ldcCode seemed to work... I guess the end result of what data I was working with and sending back I figured that I wanted the 2 way data binding.
You need to use without {{}} for two way binding
<lcd-code ldcCode="detail.program.ldcCode"></lcd-code>
You do not have to use Expression , remove the {{}}
<lcd-code ldcCode="detail.program.ldcCode"></lcd-code>
I'm building a directive that needs to initialize some data based on values passed into it through the scope. The problem is that when I try and initialize the data in the link function, the passed in value isn't available yet. Is there anyway to only run the initialization when the passed in value is available? I thought about using a watch as in the following code but it seems messy (and doesn't seem to work anyway).
.directive('etMemberActivitySummary', [function () {
return {
restrict: 'E',
templateUrl: '<div>My template</div>',
transclude: false,
scope: {
memberModel: '='
},
link: function(scope, element, attrs, controller) {
var watcher = scope.$watch(
function() {
return scope.memberModel
},
function(value) {
console.log(value);
if (value != null) {
console.log('Watch');
console.log(value);
watcher();
// Perform initialization based on scope.memberModel here
}
});
}
}
}])
Is there a correct way to do this? If it helps, the passed in value is in itself retrieved from a web service.
Update 1
Turns out that if I put an ng-if="ctrl.memberModel" on the directive usage like the following and get rid of all the watch stuff, it works. Is this the best way to do this?
<et-member-activity-summary member-model="ctrl.memberModel" ng-if="ctrl.memberModel"></et-member-activity-summary>
I've got a following template:
<select
id="someSelect"
ng-model="component.picture"
ng-controller="someChildController"
size="12"
ng-options="img.url as img.name for img in image.list | filter:img.filter">
</select>
The important part is ng-model. I want to make the code as re-usable as possible, so consider this model as one that could change anytime. The question is, how to change the value from the controller, when I cannot update $scope.component.picture directly?
Is there some way to get element's model object, no matter what is it's object name?
EDIT:
I may have not been clear. Consider the case, where in different place in the application the same template is used, but with changed model (so, no component.picture). But it's still wrapped in child controller which handles the updates of the model. I cannot call component.picture directly, because I cannot be sure it's the one in ng-model.
If everything else fail, I may need to do something like:
var _el = angular.element('#someSelect');
var path = _el.attr('ng-model').split('.');
var model = $scope;
var lastIndex = -1;
path.forEach(function(p, i) {
if(typeof model[p] === 'object') {
model = model[p];
lastIndex = i;
}
else return false;
});
model[path[lastIndex+1]] = "someNewValue";
But it's quite ugly, so I wanted to know, if there's a better way to do it.
Using a directive you can inject any kind of scope objects, doesn't matter what the namespaces are, the directive will take that data as "thisdata" creating a new specific scope it can use to display the HTML starting from the template provided.
// HTML
<test-dir component="component.picture" images="image.list"></test-dir>
// JS
app.directive('testDir', function() {
return {
restrict: 'E',
scope: { component: '=', images: '=' },
template: '<select size="12" ng-options="img.url as img.name for img in images | filter:img.filter"></select>',
link: function(scope, elem, attrs) {
console.log(scope.component);
console.log(scope.images);
}
}
})
I didn't test it!
You could use a directive with a template where you pass the list of elements and the model such as:
app.directive("newDirective", function () {
return {
restrict: 'E',
scope: {
image: '=',
component: '='
},
template: '<select ng-model = "component" size = "12" ng-options = "img.url as img.name for img in image.list | filter:img.filter" > </select>'
}
});
Here the html is stored into the template which is applied every time the directive is called and it's creating a scope for the variables:
the ngModel
the object containing the list of rendered options
In this way you can a new model every time you need it with:
<new-directive image="img" component="comp.picture"></new-directive>
Where img is the object containing the list in your controller and comp is the variable where the value is stored.
I created a jsfiddle that may help you.
JSFIDDLE: https://jsfiddle.net/vaebkkg9/2/
EDIT: I changed the code so you just use component and not component.picture. If you want to assign a property of an object you have just to use it as component="comp.picture"
I've got an Angular directive. Inside the link function, I do this:
link: function(scope, element, attrs) {
...
element.data('startY', value);
...
}
What I'd like to do is perfix 'startY' with the name of the directive, without hard-coding the name. I'd like to dynamically get the name.
Is there any way to do this? Does Angular provide a way to reflect it? Something like:
link: function(scope, element, attrs) {
...
element.data(this.$name + '-startY', value);
...
}
If not, what are the recommended best practices for choosing data() keys to avoid collisions?
As indicated in the AngularJS source code, a directive's name is assigned in the context of the object literal where your directive options reside. The link function however cannot access the object literal's context, this, because it will be transferred to a compile function where it will be returned and invoked after the compilation process has taken place.
To get the name within your link function you can follow any of these suggestions:
[ 1 ] Create a variable that may hold reference to the object literal(directive options).
.directive('myDirective', function() {
var dir = {
link: function(scope, elem, attr) {
console.log(dir.name);
}
};
return dir;
});
[ 2 ] You can also get the directive's name by using the compile function since it is invoked in the context of the directive option.
.directive('myDirective', function() {
return {
compile: function(tElem, tAttr) {
var dirName = this.name;
return function(scope, elem, attr) { /* link function */ }
}
};
});
As far as I can tell you've answered your own question. You may prefix the name by string concatenation as you've done but it will probably be easier to add it as a separate data store.
element.data('directiveName', this.$name).data('startY', value);
I'm not sure what you mean by avoid collision as this will only apply to the element that was passed into the link function.
I'm trying to build a very simple Angular directive:
app.directive("myDirective", function () {
return {
template: "{{result}} {{result.startTime}}",
scope: {
result: '#result'
}
};
});
And I use it this way in the view:
<div my-directive result="{{result}}"></div>
the problem is that {{result}} is displayed correctly (as a json) abject, while {{result.startTime}} is not displayed, despite the fact that the displayed {{result}} contains the startTime property.
2 problems:
1 You need to pass the model in, not the interpolated string.
<div my-directive result="result"></div>
2 You need to assign the value to the directive, so use '=' instead of '#' which gives you the 1-way binding from the directive back to DOM only.
app.directive("myDirective", function () {
return {
template: "{{result}}, {{result.startTime}}",
scope: {
result: '='
}
};
});
Working Demo
app.directive("myDirective", function () {
return {
template: "{{result}} {{result.startTime}}",
scope: {
result: '='
}
};
});
You're using the wrong isolate scope symbol there I believe, # will give you the string representation of the variable passed in = will two way bind, if the name is the same as the property you can leave it out and just use = as shown above.
try to use the "=" rather than the "#". The "#" brings it in as a string reference.
scope: {
result: "="
}