I'm trying to understand EmberJS template convention in Discourse.
Here's a snippet from app/assets/javascripts/discourse/templates/discovery/categories.hbs
{{#discovery-categories refresh="refresh"}}
{{component controller.categoryPageStyle
categories=model.categories
latestTopicOnly=controller.latestTopicOnly
topics=model.topics}}
{{!-- my-template --}}
{{/discovery-categories}}
What is the meaning of discovery-categories and component?
For example I want to insert my-template to extend categories.hbs, what is the convention I should use to create file with my template?
discovery-categories is the name of the component which is
called statically using the name of the component.
Whereas in the second line 'component' is a template helper which loads the component dynamically using the name specified via property controller.categoryPageStyle.
3.my-template is the yield block , where you can have context of the component discovery-categories if its yield.
for eg. if discovery-categories has a property foo you can write something like
{{#discovery-categories refresh="refresh" foo="Some Text"}}
{{component controller.categoryPageStyle
categories=model.categories
latestTopicOnly=controller.latestTopicOnly
topics=model.topics}}
{{foo}}
{{/discovery-categories}}
Related
I'm new to Angular, sorry if my questions sounds dumb. I always confused with ngOnInit() and ngAfterContentInit() lifecycle hooks. On the official docs, it says:
ngOnInit() : Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.
ngAfterContentInit(): Respond after Angular projects external content into the component's view / the view that a directive is in.
My questions are:
Q1-For ngOnInit(), what does Initialize the directive/component after Angular first displays the data-bound properties mean? Does "Initialize " means create an instance of directive/component?
Q2-For ngAfterContentInit(), what's component's view means? does the view mean the associated template html in the component's templateUrl?
Q1: No, the creation of a class instance is the method constructor which happened before ngOnInit(), a component is a directive with a template data-bound properties, view nodes etc.. and ngOnInit() is called after data-bound properties is ready, and as you may known, ngAfterViewInit() is called after view ready.
Q2: I have one example for what the "content" mean:
You define a component selector inside app.component.html with a text inside :
<custom-component>
Some random text
</custom-component>
Now inside custom-component.component.hml you can display the text "Some random text" using <ng-content></ng-content> which act as a placeholder for the text you passed down
<ng-content></ng-content>
ngAfterContentInit() simply mean the passing of "Some random text" into custom-component.component.hml view is completed.
A1) First you need to know what data-bound properties is. This answer question should help you:
What is data-bound properties?
What creates first is the constructor() The constructor comes before the ngOnInit()
https://angular.io/guide/lifecycle-hooks
A2) Yes, the view is either the xxx.component.html or the template inside of the component.ts file
https://dev.to/devpato/displaying-data-in-angular-unofficial-docs-4nd4
What is the difference between ngAfterContentInit and ngAfterViewInit?
I am using paper-data-table which is a extension to ember-paper.
Both use a technique I did not see before which I would describe as "template helpers create template helpers".
Here is a simple example of the ember-paper toolbar component
{{#paper-toolbar as |toolbar|}}
{{#toolbar.tools}}
{{#paper-button}}
Go Back
{{/paper-button}}
<h2>Toolbar with Standard Buttons</h2>
<span class="flex"></span>
{{#paper-button raised=true}}
Learn More
{{/paper-button}}
{{#paper-button mini=true aria-label="Favorite"}}
{{paper-icon "favorite"}}
{{/paper-button}}
{{/toolbar.tools}}
{{/paper-toolbar}}
There is a new template helper created {{#paper-toolbar as |toolbar|}}.
In my use-case I want to pass the row template helper which is created by the paper-data-table template helper(/component?) down to another component to encapsulate the logic inside it.
I tried to pass it down as a argument:
{{#paper-data-table
sortProp='sort'
sortDir='asc'
as |table|
}}
{{#table.body as |body|}}
{{#each questions as |question index|}}
{{question-row
row=body.row
}}
{{/each}}
{{/table.body}}
{{/paper-data-table}}
But when trying to use the helper(/component) in the template of the question-row component
{{#row as |row|}}{{/row}}
I get the following error:
Assertion Failed: A component or helper named "row" could not be found Error
So I wanted to ask if thats possible and how that would work.
This method is called contextual components and I was able to solve it with the following code in my question-row component:
{{#component row as |row|}}
{{#row.cell}}
HALLO
{{/row.cell}}
{{/component}}
What's the best way to load a ko component with JavaScript code instead of defining a custom element in html? I tried with ko.components.defaultLoader.load but my component constructor does not hit.
I double checked and the component appears to be registered.
I believe what you are looking for is function ko.components.get(componentName, callback). What this method does is ask the component loaders to resolve the component name until it finds one. If it doesn't find one, it will call callback(null). If it does fine one, it will call callback(componentDefinition), where componentDefinition is the object used to register the component, like { viewmodel: ..., template: ...}.
As far as I can tell, there isn't a ready made function which returns a "working" component. What you have to do after getting the componentDefinition object is something like:
convert the template into a DOM element
instantiate the viewmodel (if defined)
bind the viewmodel to the DOM element
Note that this is not straight away because templates and view models can be defined in several ways.
I recommend looking at https://github.com/knockout/knockout/blob/master/src/components/componentBinding.js and see how it's done here (from line 38).
I hope this works for you, otherwise you could consider other options, like dynamically creating a div element in code with a component binding where the component name and parameters are bound to properties of a view model. Then bind this view model to the div element you just created. This should work "code only" which much less code than the other route.
I wonder if I can modify a property that is in a component via an external controller.
That is, I have an injected component in index.html as follows:
{{ button-feed }}
This component is used in many views.
This component has to be hidden as I get values in the controller, and what I really want is that since this controller, modify a property that hides or shows the button.
The component has the form:
App.ButtonComponent = Ember.Component.extend ({
hideClass: false
});
The property hideClass is used to display or not the button. What I want is to modify this property but using a controller that does not belong to the component button.
I tried to access the property from outside the component, but it is impossible.
You can pass parameters to your component like this:
{{button-feed hideClass=true}}
{{button-feed hideClass=false}}
Also, you could pass in a controller property too.
{{button-feed hideClass=controllerProperty}}
To answer your comment, you can set the controllerProperty by using the code below. Since controllerProperty is bound to the hideClass on your component, changing controllerProperty will change hideClass.
controller.set('controllerProperty', false);
You can read more about setting properties on a controller here.
I'd like to render a component that I will define within a variable.
I have a variable containing content like that:
page.js
Ember.Controller.extend({
c: {
'pageTitle': 'This is the title with a component {{my-component}}'
}
});
page.hbs
<div>
{{c.pageTitle}}
</div>
The c object is populated from an API call, from a content server.
I would like to provide the capability to inject components from what is defined in the content.
Basically what I need would be to render 2 times, the first time to replace my {{pageTitle}} with the string, and the second time to replace {{my-component}} with the component.
What would be the best solution to do such a thing?
Thanks
You could render the component using the new {{component}} helper (in the latest version of Ember)
{{component 'my-component'}}
{{component c.myPageTitleComponent}}
For the text to be present, you could do two things:
In your Template
You could implement the text directly in your template
{{c.myPageTitleText}} {{component c.myPageTitleComponent}}
Using a Parent Component
You could implement a parent component:
{{my-parent-component text=c.myPageTitleText componentName=c.myPageTitleComponent}}
Then in 'my-parent-component', it would look like this:
{{text}} {{component componentName}}
This doesn't really make sense though unless you needed some custom logic in my-parent-component.