I have a component in Ember2 that receives a parameter {{my-component product=p}}. I need to pre compile the component and get the generated html.
I have tried to use ember-cli-htmlbars-inline-precompile but without success. let template = hbs"{{mycomponent product=p }};
First its important to clarify the terms. Compiling a component does not mean to produce HTML! It basically means to produce a bytecode that can be used with the glimmer runtime to produce and update DOM. Ember will never produce HTML, and this is important to understand.
If you think ember produces HTML and then gives that to the browser to render you are wrong.
Ember directly produces DOM, and then keeps track of the DOM nodes to update the DOM and allow live binding.
So basically there is no public API in ember to do what you want.
Of course you can just render the component, and use this.element.outerHTML to access the HTML. But remember that with that you will lose all ember functionality like live-binding or actions.
This is especially tricky because google maps renders all into an iframe.
ember-wormhole shows that its possible to render ember content outside the main div, but they make use of private API and I think this possibility ends with the iframe.
Related
I am using an API get call to fetch the HTML code from database and render the html.
The generated HTML is as below: -
<span (click)="triggerClick()" data-link-id="960" data-link="8" class="level_modal">individuals</span>
triggerClick() function is defined in the ts file but the same is not triggered with dynamically generated html code.
Reason
It's not going to work. The language you use to write Angular templates in is not HTML. It's a custom Angular syntax that purposely looks like HTML to make experience of writing code better. Those templates are then compiled into highly optimized JavaScript functions.
A string you get from your API is received during the run-time of your application, which means that Angular's compiler has already done its job (it has compiled the code which is why you can run your application at all). At this point, Angular's compiler is not available anymore by default. Including the compiler in the final bundle of your app is a performance issue because the compiler code has a large size footprint. This is the reason why AOT compilation (ahead of time) is the default and recommended way of publishing Angular apps for production.
Solution
Your API should not return HTML at all. The whole idea behind single page applications (SPA) is for them to consume and API which is language- and platform-agnostic. It simply works with data, which is today mostly encoded via JSON notation. It's the only language API is supposed to understand.
From what I can gather from the code example you've posted here, you're interested in data of this form:
{ linkId: 960,
link: 8,
text: individuals }
Create an API endpoint which returns data in this form, and then use Angular to loop over the data and create the template which you need.
<span *ngFor="let item of items"
(click)="triggerClick()"
[attr.data-link-id]="item.linkId"
[attr.data-link]="item.link"
class="level_modal">
{{ item.text }}
</span>
Of course, you probably do not need all those data- attributes anyway. This is because Angular application should focus on the model. Model is the source of truth which is used to create the template. If you need additional information about each item (such as link and linkId), just keep in the memory as an object. No need to encode it into HTML just to read it later again.
When you write
(click)="doSomething()"
You want to say
Launch the method doSomething when I click on this
But what you actually say is
Please Angular, compile this code so that when I click on this, it launches doSomething
Because as you may know, you're writing Typescript, not Javascript. So your code will be compiled to be served, thus transforming this into
onclick="doSomething()"
BUT WAIT, THERE'S MORE
When you compile your code, it's actually minified and uglified. So this will give you something along the lines of
onclick="a()"
And thus, making you lose any logic behind this.
So what's the solution ?
The solution is to bind the click event to a global function. There's two ways of doing this :
Function in your component (use it if you want to use logic from your component)
Global function (use it if you want this function to be called in several components)
I'll explain the first use-case, for the second I'll make it short : create a JS file as an asset, put your function in it, and call it on click.
For the second one, it's a bit tricky.
First, inject ngZone into your component : this allows you to handle code outside of Angular
constructor(private zone: NgZone) {}
Then, create a global function (I'll make it Typescript and linting compliant)
ngOnInit() {
this.zone.run(() => {
window['doSomething'] = () => {
console.log('Look mom, I did something !');
};
});
}
After this, delete the function once you leave the component
ngOnDestroy() { delete(window['doSomething']); }
Finally, change your HTML from this
(click)="doSomething()"
To this
onclick="window.doSomething()"
I want to create an in-repo addon to make certain modifications (styles, templates, etc.) to an existing ember app in an encapsulated way, but I'm having troubles overriding the templates.
Right now, I'm trying to override an existing component template with the template from a component with the same name in the in-repo addon. My code looks something like this:
// my-app/app/templates/components/foo.hbs
<h1>Some headline<h1>
// my-app/app/lib/my-addon/app/templates/components/foo.hbs
<h1>A different headline<h1> // -> this never shows up
I've tried a lot of switching around the template structure (like putting it in /addons or /app and linking to the template in different ways, but without success. My problem is that ember never uses the template from the addon.
If the component within the addon has a different name, like foobar.hbs, I can call it without a problem.
I'm currently looking through the source code and docs, trying to make sense of this. Is this even accomplishable the way I imagine it?
Thanks a lot!
You'd have to create the component in your ember app which, initially, will mean the component renders as nothing as it's a brand new, empty component. Then you'd dig into your node_modules, find the component file and template and copy over what you'd need to work with.
Here's an example. While working with ember-cli-jsonapi-pagination, I need to customize the paginate-collection component:
I created the component in my application.
I looked at the source: https://github.com/BookingSync/ember-cli-jsonapi-pagination/tree/master/app
In components/paginate-collection/component.js I copied over the component code, but you should be able to import it as well.
In components/paginate-collection/template.hbs I modified the template as needed.
Is it possible to create javascript elements like OpenStreetMap or jQuery inside a vaadin application?
Because vaadin websites are created by programming in java and letting the compiler autocreate the DOM and JavaScript out of it?
So, is it possible at all?
You can create such an integration with AbstractJavaScriptComponent
The basic idea here is to subclass this class, annotate with #JavaScript to pull in the needed JS libs. Then write at least a global function, that sets up your lib in the DOM (you will have a <div> at your disposal). Your component can hold state, the server side can call defined functions on the client (while sending e.g. state) and the client can call server functions (params passed as JSON).
The Wiki has an example how to include such a component
There are some easy and cheap solutions which are not the best in the long term, but they may be enough:
1)
If you just want to render some html you cant insert it as the value of a label and change its Content Mode to html.
https://vaadin.com/book/-/page/components.label.html
2)
If you just want to execute some javascript code after a ui event you can call Javascript.getCurrent().execute(javascriptCode).
https://vaadin.com/book/vaadin7/-/page/advanced.javascript.html
Notice that if you are trying to do some re-usable components, this is not the right answer
I am new to Ember, so this might be a stupid question, but bear with me. Is there a specific reason one can't render templates with a dynamic string? Specifically, I push various objects to a controller variable like this:
this.controllerFor('application').get('popups').pushObject({
resource: article,
template: 'article'
});
And then try to render them:
{{#each popup in model}}
{{ render popup.template popup.resource }}
{{/each}}
This doesn't work in Ember as-is, since it expects a string as the template. I just patched this in my ember source in the renderHelper function:
if(name.value) name = name.value();
This makes sure that if the name comes from a property, it gets converted to a string correctly. It works perfectly fine. What is the reason Ember doesn't support this out-of-the-box? Am I missing something?
A little background for the stuff above: I want to open lots of different resources in popups on the page, but I want to keep the popups separated from the rest of the current route so I can show them on their own page if needed without duplicating code. My idea was that I push all open popups to a global array, and they are rendered at the bottom of the page (A bit like we do it at the moment without ember). Maybe there is a better way to do this that I missed!
You want to
Create a popup template independent of route
Pass data to template and render it any where
Right ? If yes there is a very good concept of component in Ember.js
Component is a small module with is separate from application. It only depends upon the data passed into it.
It can be reused in different portion without ever effecting each other. They are especially good to run third party plugins like qtip etc.
here is good article about use case of component.
Basically I want to do something like Rails content_for, part of a view looks the same everywhere, but some parts of it are different depending on where you are in the site.
Here is an example of what I want: http://emberjs.jsbin.com/sikimayo/2/edit
If you run that and check you javascript errors, you will see this error:
Cannot read property 'connectOutlet' of undefined
It seems like foo-with-outlet isn't an active view, since it was rendered by the application template, not the current route.
Although not ideal from a perspective of eliminating boilerplate in each sub-route, this can be achieved by nesting outlets and their associated render calls.
See JSBin http://emberjs.jsbin.com/sikimayo/4/edit