I'm trying to use the new components system in knockout 3.2.0.
There isn't much documentation at the moment, but this does work.
ko.components.register('price-input', {
template: '<span>price-input</span>'
})
However the template binding allows you to specify a template name that already exists in the DOM, such as:
<script type="text/html" id="price_input">
<span>price-input</span>
</script>
Then you could do this:
<div data-bind="template: {name: 'price_input'}"></div>
So I tried this:
ko.components.register('price-input', {
template: {name: 'price_input'}
})
but it doesnt work. Is there a way to use named templates with the new components, or must they be inline or loaded with AMD.
Thanks
Edit: After RP Niemeyer's answer, for clarification here is the template I tried his answer with:
<script type="text/html" id="ifx_price_input">
<h4>PRICE INPUT <span data-bind="text: value"></span></h4>
</script>
Here is the component code:
ko.components.register('price-input', {
template: {element: 'ifx_price_input'}
})
It does load the template, but treats it as an escaped string.
Ideas?
You can pass an element property that is either an element itself or a string that is the id of the element like:
template: { element: 'myTmpl' }
In v3.2.0 beta, this case wasn't handled well, hence the hackery needed by InternalFX.
This will be fixed in v3.2.0 final. It will work as you expect - simply reference a script, template, or textarea element, and its logical contents will be intepreted as template nodes.
In case you're interested, the commit that fixes and tests this is here: https://github.com/knockout/knockout/pull/1454
Finally solved this with some hackery...I hope this gets answered better by the knockout devs.
This works. Basically I just load the template text manually and pass it to the register function. So it works as if it was inline.
ko.components.register('price-input', {
template: $('#ifx_price_input').html()
})
Related
I have a template which is nested inside another template which I want to load when i click on a button.
So the nested template is loaded dynamically. This is what I have done so far.
This is the main body.html (this loads when a url is provided in the browser e.g. http://url#/newtemplate)
<div ui-view> </div>
Other section of the code has been removed for brevity
This is the new_template.html which I expects it to show when I click a button.
When I put a template name directly like below i.e. when I hard code it
<div ui-view="number1"></div>
It loads the template fully.
This is the dynamic model
<button ng-model="template_name" ng-value="number1">Button1</button>
<div ui-view="{{template_name}}"></div>
{{template_name}}
The above does not load the template as I expected. but it shows the string number1 when
the button is clicked
What can I do for it to load the template....
This is my controller
.state('parent',{
url: '/newtemplate',
views:{
'':{
templateUrl: "parent.tpl",
contoller:"controller",
},
'number1#parent':{
templateUrl:"number1.tpl",
contoller:"formcontroller"
},
'number2#parent':{
templateUrl:"number2.tpl",
contoller:"formcontroller"
},
'number3#parent':{
templateUrl:"number3.tpl",
contoller:"formcontroller"
}
}
})
Strange enough when I used the dot notation it did not work so I have to use the absolute naming method.
I also noticed that when I added the nested views as shown above the time it takes before the template gets loaded take a very long time.
Please I would appreciate any help which can allow me to load a nested view at runtime (possibly very fast)
Expecting more answer
I still hope that the I can make use of ui-view/ui-router because of the ability to make use of controller.
I'm not sure you can use uiView to load html dynamically.
I would try another possible solutions:
Use directives
Using ngInclude
I'll leave you an example with ngInclude: https://next.plnkr.co/edit/M5hl71mXdAGth2TE?open=lib%2Fscript.js&deferRun=1&preview
I know that a angular directive can be define in four ways:
'A' - only matches attribute name
'E' - only matches element name
'C' - only matches class name
'M' - only matches comment
For example a directive with the restiction "M":
angular.module('exampleApp', [])
.directive('myDirective', function() {
return {
restrict: 'M',
...
};
});
and declaring the directive in HTML
<!-- directive: my-directive -->
But why would anybody use the M restriction for a directive? I find this really strange. Because if i comment out code, i don't want it to run. So why is this a thing?
From the docs:
Best Practice: Prefer using directives via tag name and attributes
over comment and class names. Doing so generally makes it easier to
determine what directives a given element matches.
Best Practice: Comment directives were commonly used in places where the DOM API limits the ability to create directives that spanned multiple elements
(e.g. inside elements). AngularJS 1.2 introduces
ng-repeat-start and ng-repeat-end as a better solution to this
problem. Developers are encouraged to use this over custom comment
directives when possible.
It is not good practice to use it now.
According to this post it is used usually only for backwards compatibility and for passing markup validations.
An example of how to use it is below
(function() {
angular
.module('exampleApp', [])
.directive("comment", function() {
return {
restrict: 'M',
replace : true,
template : "<h1>Made by a comment directive!</h1>",
link: function(scope, element, attrs) {
console.log(attrs.comment);
}
};
})
})();
<!DOCTYPE html>
<html ng-app='exampleApp'>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
</head>
<body>
</body>
<body>
<!-- directive: comment comment argument string -->
</body>
</html>
Because if i comment out code, i don't want it to run.
This is not HTML you're talking about, it's an Angular convention/feature. The comment is still visible/accessible inside the DOM as a comment node. The HTML parser won't do anything with it, true. But Angular can still find and parse and act on it. So, yes, it's another way to declare a directive which doesn't have any side effect on the DOM. You have to very explicitly use the directive: ... syntax inside a comment; you probably won't be triggering this by accident with code you simply want to comment out.
Here is my case:
I have a div which needs to get some html injected from a function:
<div ng-bind-html="myCtrl.getMyLink()"></div>
in my controller I have:
this.getMyLink = function () {
return '<a ui-sref="app.go.to.state1">my link</a>';
}
It works but not at all. All I have at the end in my html is only
<a>my link</a>
It's a link but the ui-sref redirection is not working.
There is surely something I'm missing.
How can I fix that?
ng-bind-html does not work with ui-sref directive. Just use href="path/to/state1" instead
The content inside ng-bind-html doesn't get compiled by angular, it is intended for static html.
You would need to use your own directive instead and either set a template or do your own compiling with $compile.
Alternatively you might be able to use ng-include. There aren't enough details given for your use case to help much more
I'd like to use ARIA attributes in Ember core form components, such input and textarea fields.
I noticed that using an aria attribute within the component in my template, it doesn't work at all
{{input aria-label="Your name"}}
{{textarea aria-label="Your address"}}
So I decided to reopen the core components in an initializer to add this attribute to the components
export default {
name: 'reopenTextAreaComponent',
initialize: function () {
Ember.TextArea.reopen({
attributeBindings: ['aria-label']
});
}
};
Since I did that, the performance of my application is pretty bad. The integration tests take much more time than before.
I tried not to use their components and simply a HTML tag:
<textarea {{bind-attr aria-label="Your address"}}>{{value}}</textarea>
But this doesn't compile with handlebars! It returns an error because of the {{value}} within the textarea tag.
What is the solution to avoid reopening? Should I create my own component?
Thanks
From Ember 2.8+, the simplest way of doing this is to install ember-component-attributes, like this:
ember install ember-component-attributes
Then you can add aria-label, other ARIA attributes and whatever other attributes you might require as follows:
{{input (html-attributes aria-label="Your name")}}
{{textarea (html-attributes aria-label="Your address")}}
What is the solution to avoid reopening? Should I create my own component?
Those are your two options.
I think you have the correct approach regarding the reopening of Ember.TextArea, because you want to have aria-label available on all instances.
I use a similar initializer - with one difference being that I reopen Ember.TextSupport so that both Ember.TextField and Ember.TextArea will have the aria-label binding.
// initializers/input.js
import Ember from 'ember';
export function initialize(/* application */) {
Ember.TextSupport.reopen({
attributeBindings: ['aria-label']
});
}
You could try creating your own component(s) to see if there is any difference in performance, but I think reopening is the right approach in this case.
I have a controller like this:
#VariantModalCtrl = ($scope) ->
$scope.upload_variant_image = ->
alert("test")
When I try to call upload_variant_image function using ng-click, it only works when binding to a static DOM (when the DOM loads), I have a link like this:
<%= link_to "test", "" , "ng-click" => "upload_variant_image()" %>
but this element is dynamically added after the DOM is loaded, so ng-click doesn't work.
Update
Just found part of my answer using $compile function:
AngularJS + JQuery : How to get dynamic content working in angularjs
BUT it doesn’t work when I update the DOM like this in Rails:
$(".modal-body").html($compile("<%= j render("/variants/form", :variant => #variant) %>")(scope));
I would warn you that you may not be fully embracing Angular philosophy if you're manipulating the DOM through Angular-external means. Adding links dynamically with AngularJS is as simple as anything else and will probably be much easier and more idiomatic than getting your external library to play nice. Here is a simple example based on your question:
<ul>
<li ng-repeat="item in items">
<a ng-click="upload_variant_image()">{{item.name}}</a>
</li>
</ul>
All you need to do is wire up the scope data appropriately and this will always "just work."
That said, if you are manipulating the DOM, you can cause AngularJS bindings to occur (to, say, ng-click as you desire) by using $compile service. Do consider the above better option, though. :)