Access elements inside of a remote modal with vue.js - bootstrap-modal

I'm read through the vue.js docs, but I am confused about how to load a partial and bind it to the vue instance.
As an example, with bootstrap's js, I can load a remote modal like this:
Add New Campaign
This loads a partial inside of my
<div id="modal></div>
Using vue, I have made the modal a component, and the partial will load, but I cannot get the partial's elements to bind to the vue instance. So any events inside of the modal such as
v-on:click.prevent="say('hi')"
won't work.
How do I bind elements added to the page after the vue instance is created?

You could use $mount
Parent vue instance declaration:
var vm = new Vue({...})
New Instance: (have this run after the dynamic content is loaded into your container)
var newContentInstance = vm.$addChild({
el: '#modal',
data: function data() {
return { something: '' };
}
});
newContentInstance.$mount();

Related

Is it possible to have Livewire events in a Datatables render function

I'm trying to open a modal and populate a form field with a model value.
The button to open the modal is embedded in the datatable config js and I'm thinking Livewire is running before the table is built.
The datatable uses ajax to populate and columnDefs to configure rows.
The JS for the config of the datatable is in a #push('scripts') in the component blade file.
The (simplified) render function for the row is:
var datatable = thetable.DataTable({
.....
render: function (data, type, full, meta){
return ('Edit');
},
.....
})
Clicking the button opens the modal but no XHR is made to the components edit function.
If I create an anchor in the same blade file as "normal" html:
<a href="#" wire:click="edit({{ 25 }})">test</button>
The XHR is called as expected.
So, I'm assuming Livewire doesn't know about the button - perhaps it ran before the table was built. Is there a way to force Livewire to wait?
I have tried to emit a Livewire event instead of wire:click = also no XHR.
I have seen the couple Laravel packages for Datatables. They don't suit.
I got this done with emitted events and component listeners.
jQuery's on to bind to the future elements clicks which livewire.emit the events.
protected listeners[] in the component respond to the emitted events.

Managing 'window' object in Isomorphic App with ReactJS and Flask Python

I am developing an application with Flask Backend with ReactJS front.
ReactJS app has been developed and bundled with webpack.
Everything works fine with client side rendering which is bundled with webpack.
I am now trying to add server side rendering with python-react .
But problem is, I have to share some variables to my ReactJS app via Jinja2 template in base template index.html which has the reactjs root component node <div id='react-node'></div>.
I had to send my routes and config to my application via jinja2 template like below ,
//index.html
<!doctype html>
<html>
...
...
<script type='text/javascript'>
var STATIC_IMAGE_ROOT = "{{ url_for('static', filename='img/') }}";
var ROUTES = { ... };
...
</script>
</html>
All the above js variables are being set to global window object .
But when I am trying to render the component in python, it throws exception for window object ReactRenderingError: react: ReferenceError: window is not defined .
What is the best way to solve this issue ?
There is no window global when rendering on the server. You can create a fake window, first checking if the window exists:
if (typeof(window) == 'undefined'){
global.window = new Object();
}
Alternatively, you can use jsdom, or a similar library to create a fake DOM.
Just add the following to webpack config:
// By default, Webpack is set up to target the browser,
// not a Node environment. Try setting target in your config:
target: 'node',

how to inject a store into a component (when using localstorage adapter)

Ember docs say to define a store like this
MyApp.Store = DS.Store.extend();
If you are looking up records in components, this doc says you can inject the store into the component like this
// inject the store into all components
App.inject('component', 'store', 'store:main');
However, I am using the local storage adapter which I define like this
App.ApplicationAdapter = DS.LSAdapter.extend({
namespace: 'my-namespace'
});
Therefore, I don't know how to inject this into the component (where I need to look up a record) following the above instructions.
Following the instructions of this SO answer, I tried to inject the store into a component by passing it in like store=store and/or store=controller.store
<li> {{my-component id=item.customid data=item.stats notes=item.notes store=store}} </li>
or
<li> {{my-component id=item.customid data=item.stats notes=item.notes store=controller.store}} </li>
The goal was then to be able to do this in an action in the componeent
var todo = this.get('store');
console.log(todo, "the new store");
todo.set('notes', bufferedTitle);
console.log(todo, "todo with notes set");
todo.save();
However, todo.save(); always triggers
Uncaught TypeError: undefined is not a function
Notice that I logged the store? this is what it shows
Class {_backburner: Backburner, typeMaps: Object, recordArrayManager: Class, _pendingSave: Array[0], _pendingFetch: ember$data$lib$system$map$$Map…}
If i inspect it(by opening the tree, which isn't shown here), it does indeed show that notes were set via todo.set('notes', bufferedTitle); however, it doesn't have any of the other attributes of my model that I defined for the index route, and this object doesn't have a 'save' method. Therefore, it doesn't seem to be the actual store, but rather just some backburner object.
I got the same results trying this SO answer where it says to get the store of the targetObject
var todo = this.get('targetObject.store');
Note, I also tried this, i.e. setting the store to be the store of the item.
<li> {{my-component id=item.customid data=item.stats notes=item.notes store=item.store}} </li>
It should be noted that if I set the store in the component, I can print the store on the page by doing {{store}} which gives me
<DS.Store:ember480>
but I can't do var todo = this.get('store'); in the action that handles the click even in the application code.
Question, using the localStorage adapter, how am I able to look up a record in a component (with the aim of then being able to alter the record and then save it again)
Note, if it's important, I define a model for the (index) route like this
App.Index = DS.Model.extend({
title: DS.attr('string'),
version (unfortunately I don't know what version of Ember data or the adapter I'm using)
Ember Inspector
1.7.0
Ember
1.9.1
Ember Data
<%= versionStamp %>
Handlebars
2.0.0
jQuery
1.10.2
Update in response to request for more info
The code that sets up the problem is very simple.
here's the router (with a bad name for the resource :)
App.Router.map(function(){
this.resource('index', { path: '/'});
}
Here's the route that gets the record to use in the Index route
App.IndexRoute = Ember.Route.extend({
model: function{
var resource = this.store.find('index');
return resource;
}
});
I have an Index Controller which does nothing in particular for the component (unless I should be defining methods on the Controller that get triggered by component events)
In the html, I do this with handlebars to pass data to the component
{{#each item in items}}
<li> {{my-component id=item.customid data=item.stats notes=item.notes store=store}}
{{/each}}
Then, in components/my-component, I have a label that when clicked is supposed to trigger an action that will let me edit one of the attributes on the model
<label> {{action "editTodo" on="doubleClick">{{notes}}</label>
that click triggers this code in App.MyComponent, which triggers the error that prompted this question
var todo = this.get('store')
todo.set('notes', bufferedTitle);
todo.save()
IMHO injecting store into components is not the best idea... By design, components should be isolated and shouldn't have any knowledge about the store.
In the doc you've given, it's written: In general, looking up models directly in a component is an anti-pattern, and you should prefer to pass in any model you need in the template that included the component.
However, if you really need it for some reason, then why not just to pass the variable store to the component?
{{my-component store=store}}
Then, you can pass the store from your controller only in the components where you really need that.
Injecting the store in all your components will most likely lead you to the bad design (although it seems tempting at first).
Here's an updated answer for Ember 2:
Ember Data's store is now a Service, and we can easily inject it into all Components via an Initializer, e.g. app/initializers/inject-store-into-components:
export function initialize(application) {
application.inject('component', 'store', 'service:store');
}
export default {
name: 'inject-store-into-components',
initialize,
}
Then, in your Components, you can access the store with this.get('store'). The obviates the need to directly pass the store as an argument to Components, which requires a lot of boilerplate in your templates.
Whilst the accepted answer is sensible for simple applications it is perfectly acceptable to inject a store into a component if that component doesn't have a relationship with the url, like side bar content or a configurable widget on a dashboard.
In this situation you can use an initializer to inject the store into your component.
However, initializers can be a pain to mimic in testing. I have high hopes that the excellent Ember.inject API that is testing friendly will extend beyond services and accommodate stores. (Or that stores will simply become services).
According to this docThe preferred way to inject a store into a component is by setting a store variable to the record, for example
{{#each item in arrangedContent}}
<li> {{my-component store=item}} </li>
{{/each}}
Then in application code, you can do
var store = this.get('store');
store.set('todo', bufferedTitle);

Emberjs outlet inside a bootstrap popover

I'm using bootstrap popover in my app and I need to render an outlet inside it.
I have this nested route :
this.resource('pages', function(){
this.resource('page', { path: ':id' }, function(){
this.resource('edit', function(){
this.resource('images', function(){
this.resource('image', { path: ':image_id'}, function(){
this.route('edit');
})
});
});
});
});
When the user is here => /pages/1/edit/ when he click on an image it route to /images but render the {{outlet}} inside the popover like this :
<div class="popover-content hide">
{{outlet}}
</div>
This is my popover initialisation :
$img.popover({
html: true,
content: function() {
return $('.popover-content').html(); //need to have the outlet here
}
});
So far, it render correctly my outlet, but inside the images template, I have some button that modify the DOM and it doesn't update the html. Unless if I close and open the popover again I can see the modification.
Is it possible to render the outlet directly inside the code ? or is it possible to have my popover being updated ?
Thanks for the help.
See these links for an alternative approach to putting Ember stuff in Bootstrap popovers:
Bootstrap Popovers with ember.js template
https://cowbell-labs.com/2013-10-20-using-twitter-bootstrap-js-widgets-with-ember.html
Ember and Handlebars don't like this because it's basically copying the html content of a div and plopping it into another. But that html alone isn't everything that's needed. Ember is magic and there's lots of stuff happening in the background.
Your hidden div is real ember stuff, so let's try not to mess with it by calling .html() on it. My idea is to literally move the DOM itself instead.
first, modify your popover function call to always create this placeholder div:
content: '<div id="placeholder"></div>',
next, detach the content div from the dom in the didInsertElement of the view:
// get the popover content div and remove it from the dom, to be added back later
var content = Ember.$('.popover-content').detach();
// find the element that opens your popover...
var btn = Ember.$('#btn-popup-trigger').get(0);
// ... and whenever the popover is opened by this element being clicked, find the placeholder div and insert the content element
// (this could be improved. we really just want to know when the popover is opened, not when the button is clicked.)
btn.addEventListener("click", function() {
content.appendTo("#placeholder");
});
since the content div is immediately detached when didInsertElement is called, you can remove the "hide" css class from the content div.
edit: i tried this on my own project and it broke two-way binding. the controller updated my handlebars elements, but any two-way bound {{input}} helpers did not update the controller/model. i ended up using a single-item dropdown menu, and used this to prevent the menu from closing too quickly:
Twitter Bootstrap - Avoid dropdown menu close on click inside

ItemController does not work when we have a seperate controller for View

Part of learning Ember.js I am trying to create a Table View in Ember, based on example by Adam.
The issue I am facing is that if I create a seperate controller for the View and include an itemController for the Rows, ember gives following error: Uncaught TypeError: Cannot call method 'lookup' of null ember-1.0.0-rc.6.js:13933
When I debug this I find that in the following code :
controllerAt: function(idx, object, controllerClass) {
var container = get(this, 'container'),
subControllers = get(this, '_subControllers'),
subController = subControllers[idx];
if (!subController) {
subController = container.lookup("controller:" + controllerClass, { singleton: false });
The container is retrieved as null.
Whereas when it is run through ApplicationController, no such issue is there.
JS Fiddle Using ApplicationController for the View and another controller for itemController - Works Fine
JS Bin
in this the item Controller is specified as {{#each controller itemController="tableRow"}} and the controller is App.TableRowController
Here is a very similar JS Bin, JS Fiddle using a seperate TableViewController :
The item controller is similarly specified as {{#each controller itemController='tableRow'}.
A seperate Controller for Table View is binded using : {{view App.TableView controllerBinding="tableViewController"}} and this tableViewController is specified as a property in ApplicationController as :
App.ApplicationController = Ember.ArrayController.extend({
tableViewController: function() {
var tc = Ember.get('App.TableViewController').create();
tc.set('content',Ember.ArrayProxy.create({
content: Ember.A(tableData)})
);
return tc;
}.property()
});
But for some reason, the itemController does not work here.
Here is the JS Fiddle Using seperate Controller for View, but without any itemController - this works fine
Is there anything I am missing in the controller ?
Please help. Thanks.
With Ember most of the time you don't create objects directly, you declare the classes for things like controller, model, etc. And ember creates these objects using an IOC container. Avoid things like Controller.create. Similarly avoid directly controllerBinding instead use needs.
So, instead of providing a controllerBinding pass the content to be rendered by the App.TableView.
{{view App.TableView contentBinding=content}}
The setup of the tableData also belongs in a model() hook. It works in the sample because tableData variable is in scope.
Here's the updated jsfiddle.