How can I access ember component's controller from another controller?
Is there a backdoor way like __container__.lookup just to know that there is this instance of component?
When I tried __container__.lookup with "component:<myComponentname>" it gave me an instance of component which is not being used anywhere in the application.
TL;DR
How to test Ember Component objects?
These lines of ember-inspector reveal how to get a "view registry," a mapping of DOM element ids to their respective Ember.Component instance in Ember 2.x, or Ember.View instance in 1.x.
viewRegistry: computed('application', function() {
return this.get('application.__container__').lookup('-view-registry:main') || View.views;
}),
So if you have an Ember component in your DOM like
<span id="ember1234">Hi I'm controlled by Ember</span>
you can look it up like
var viewRegistry = application.__container__.lookup('-view-registry:main') || View.views;
var myComponentInstance = viewRegistry.ember1234;
Related
Here is my setup, and I know its not ideal (I inherited this project from a couple of developers that are no longer working on this web app). I'm trying to marry Vue.js and JQuery so that I can simply re-use JQuery based components (instead of rewriting them) on my Vue.js page (the two previous developers having design differences).
What I need is this: I am filling a table with components post page Mount. Everything about this table populates correctly except I have buttons in some of the cells in which I would like to use Vue.js to operate those buttons' functionality. I can add a v-on:click= attribute just fine but since these attributes are added to the buttons post Mount their Vue attributes are not being listened to. On the client side (Edge/Chrome in this case) I can see the vue attributes in the developers console when inspecting the element. I looked up examples such as this article:
How to make Vue js directive working in an appended html element regarding how to correctly re-render components after they are appended post Mount. Since these objects were created and appended to the web page after mounting they are not Reactive.
The article suggests creating a component that can then be mounted manually, using a template to create the component. I've tried this and found that using Vue.extend to create a new subclass/component now disallows me to access methods in my original app. Let me place some code and then I'll recap:
What I see on the client side console:
<button type="button" class="results_table_col_1_button" v-on:click="test()">200-715-122-306-ATP</button>
How the data table is being updated via JQUERY
display_search_results: function (data) {
data = JSON.parse(data);
let num = data.row_order.length;
if (num > 0) {
displayDataTable(data);
$("#tableContainer").show();
$("#test_count").text(num + " test" + (num === 1 ? '' : 's') + " found");
$('#dataDisplayTable tr').not(':first').each(function (i, el) {
let save_id = $(el).find('td.results_table_col_1').text()
$(el).find('button.results_table_col_1_button').attr("v-on:click", 'test()')
What I tried via the article
//How the new component is being created:
var testComp = new ATP_Button().$mount()
document.getElementsByClassName("results_table_col_1")[i].appendChild(testComp.$el)
//with the constructor:
var ATP_Button = Vue.extend({
template: '<button type="button" v-on:click="test()">Is this working</button>',
})
This creates the button but test is then undefined as it is not initialized or referenced to in the subclass object ATP_Button. How do I pass my 'main' Vue app's test() function to this component so that when the button $emits its event the parent's test() function is called?
I also tried adding props to the constructor so that I could maybe pass test() into the component upon creation like this:
var ATP_Button = Vue.extend({
props: ['test'],
template: '<button type="button" v-on:click="test_child()">Is this working</button>',
methods: {
test_child: function () {
this.test
alert(this.test)
}
}
})
And created the component as such:
var testComp = new ATP_Button(this.test_func).$mount()
Now however this.test inside the component is undefined as it would seem that passing the function through is not working properly (probably due to developer error aka me).
RECAP
A JQUERY table object is being populated via a backend process with HTML elements to be displayed on the front end view of the webpage
One element that is added is a button that needs to be able to call a Vue.js method from the main app
The elements are added to the table after the page has been mounted thus they are not reactive
Trying to manually mount the component creates a subclass object that does not have access to the main app's methods
Trying to pass the main app's method test() to the component during construction failed and left the test variable undefined
Eventually I need the buttons to call a method from the main app with dynamic variables inside as arguments
I appreciate anyone who is able to offer any assistance.
I'm creating a single page application (SPA) that uses Vue Router, therefore it is comprised mostly of Vue components with one blade component that puts it all together using
<router-view></router-view>
I want to know how I can pass data computed within my controller and pass it to a Vue component. The current way I'm doing it is by exposing extra API endpoints, for example in my controller I have:
public function countUsers()
{
$userCount = DB::table('users')->count();
return $userCount;
}
Then in api.php:
Route::get('usercount', 'UserMController#countUsers');
this way I can get the data within my Vue component using axios.get call to usercount.
Is there a better way of doing this? The data seems to take 1-2 seconds to display on the page and I can't imagine having this implementation for over 20 computations I need to do.
I've seen another method where you attach the data into the JavaScript context using the blade template, but I'm not sure how to get that to work for a SPA with Vue Routers.
get userCount in your controller and pass it to normal blade file. You can pass the variable in vue like below.
<router-view userCount="{{userCount}}"></router-view>
then for accessing userCount variable in vue, you can load this variable from props.
export default {
props: ['userCount'],
name: 'router-view',
},
mounted:function(){
let a = this;
this.userCount = JSON.parse(this.userCount)
}
for more information you should read the documentation first. It will help you understand thoroughly.
https://v2.vuejs.org/v2/guide/components-props.html
how can I dynamically create a component in ember 1.13.13. I want to create it programmaticaly, inside another component.
This used to work in Ember 1.11.
var component = this.get('container').lookupFactory("component:my-component");
var view = this.createChildView(component, {param1: 'param1'});
view.createElement();
Ember.$('body').append(view.element);
view.didInsertElement(); // manually call didInsertElement
The same piece of code doesn't work in ember 1.13+, and although it doesn't throw any errors, the component's div element is empty. It is like the template is not rendered. Thanks you for your help.
Thanks for the suggestions I received in the comments. I decided to rethink my components and use the each helper to get around this. I would advise everybody else to do the same.
Angular 1.5 component communication suggestions usually have output bindings to invoke methods on root controllers.
Let's say I have a root component, and two child components.
<root>
<child-1></child-1>
<child-2></child-2>
</root>
It'd like to react to a button click on component one by reading a value on component two and then doing something in the root.
For example, child-1 is a directive which wraps a drawing library that attaches a drawing to its DOM node and has a variable to control that drawing.
child-2 has a button. When it is clicked, data from the child-1 variable should be passed on to root which does something with it.
Specifically, child-1 wraps var graph2d = new vis.Graph2d(container, dataset, options);. Later on, I would like to retrieve some information from graph2d and pass it on to root to do something with it.
This boils down to: how can components react to events issued by other components? The inputs and outputs suggestions don't seem to cover that scenario.
In angular 1.5 you can use require and/or property bindings (input/output) to communicate.
If you use the require property then your root component would publish an api and your child component would get a reference to the controller:
angular.module('app').component('child1', {
bindings: {},
require: {api: '^root'}, //your <root> component
template: '',
controller: controller
});
You can then use the methods of the root component in your child component:
$ctrl.api.addWatchedBook();
This is the root component controller function:
$ctrl.addWatchedBook = addWatchedBook;
function addWatchedBook(bookName){
booksWatched.push(bookName);
}
Here is a complete architectual overview: Component Communications
This is an architectural question about Backbone JS:
AppView that contains a DOM element placeholder that is loaded with LeadsView.
LeadsView contains a DOM placeholder to present LeadView
My current route is #app/leads/1 which means that all 3 Views are loaded. AppView->LeadsView-> LeadView of lead #1.
Now suddenly the user hit the refresh button of the browser. The router would try to take it to #app/leads/:1 which routes to "app/leads/:lead_id" : "showLeadView", but AppView and LeadsView has not been rendered this time, hence the rendering of LeadView will fail.
It's looking for the DOM element to render itself into, but cannot find it.
How is that handled with Backbone?
TIA,
Nimrod.
You can programmatically initialize and render the other views when the "app/leads/:lead_id" : "showLeadView" route is hit.
The problem now becomes: How to find if a view was rendered or not ?
I solved this by adding a new property to my Models and Collections that would tell me if they were populated (either if they were fetched or if they have attributes set). An implementation for this can be found in the Thorax library repo.
And it looks like this:
isPopulated: function() {
/*jshint -W089 */
// We are populated if we have attributes set
var attributes = _.clone(this.attributes),
defaults = getValue(this, 'defaults') || {};
for (var default_key in defaults) {
if (attributes[default_key] != defaults[default_key]) {
return true;
}
delete attributes[default_key];
}
var keys = _.keys(attributes);
return keys.length > 1 || (keys.length === 1 && keys[0] !== this.idAttribute);
}
With this method i can verify if the collection needed to render the parent views has been populated. If it was't than call the methods that would be called if you navigated to app and then to leads (you practically access the routes programmatically).
It could look something like this:
if(!appModel.isPopulated())
this.showAppView();
if(!leadsCollection.isPopulated())
this.showLeadsView();
As you can see this means keeping references to appModel and leadsCollection in your router.
Update (clarification)
In the first line of my answer I was mentioning something like this:
AppView has a reference to a sub-view LeadsView that has a reference to a sub-view LeadView.
in your router you have an instance of AppView.
you pass appModel to AppView and call .render() on it when you hit app router.
you pass leadsCollection to AppView.getLeadsView() and call .render() on it when you hit leads route.
you pass the right lead model from leadsCollection to AppView.getLeadsView().getLeadView() and call .render() on it when you hit the route.
Or you can use something like Thorax
It already adds handling for subviews (takes care of event delegation, event destruction, DOM cleaning, etc.) and offers template handlers for it so you can point in your template where you want the sub-view to be rendered.
I hope now, it is clearer what I meant and as you can see it answers your question.