How do i get the container name of a component within itself?
this.componentNameStream used to kind of work in 1.11.
// in components/my-component.js
export default Component.extend({
layoutName: "components/my-component",
partialName: function() {
//return "my-component"; somehow....
}.property();
});
why? for subclassing reasons:
// in components/blah.js
export default MyComponent.extend({});
// in templates/components/my-component.hbs
<div class="someLayout">
// partialName is now "components/blah"
{{ partial partialName }}
</div>
Not sure I fully understand what you are trying to do but the usage of {{partial}} is not recommended (see https://github.com/dockyard/styleguides/blob/master/ember.md#templates).
Why not simply share a template between two components using layoutName?
// in components/my-foo.js
export default Component.extend({
showSomething: false
});
// in components/my-bar.js
export default MyFooComponent.extend({
layoutName: "components/my-foo",
showSomething: true
});
// in templates/components/my-foo.hbs
<div class="someLayout">
{{#if showSomething}}
I am the bar component!
{{/if}}
</div>
If you really need to use a partial, you could replace showSomething by a property which contains the name of the partial to be displayed as a string.
Related
could it be that Inertia.js page components are blocking the reactivity of vue?
I have a Page component, in this component is a normal single file component.
I have a function that adds items to the ItemsManager.items object.
When I'm running this function the single component below doesnt adds this items in the v-for.
But when I'm reload the Page Component it works and the previously added items appear.
Here the single file component:
<template>
<div>
<div v-for="item in items" :key="item.$key">
test
</div>
</div>
</template>
<script>
import { ItemsManager } from "./utils.js";
export default {
name: "test-component",
data: () => ({
items: ItemsManager.items
}),
};
</script>
utils.js:
export const ItemsManager = {
items: [],
add(item) {
item.$key = this.items.length;
this.items.unshift(item);
},
};
function that adds the items (in page component):
addItem(title, options) {
ItemsManager.add({
name: title,
options: options
});
},
Thanks in advance!
Since you're using Vue2, you need to know that there are some caveats when adding/deleting things to Objects/Arrays. You don't show any code relevant to your actual way of adding stuff to your object, but I can still recommend that you'd check this page to understand and fix your issue.
https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
Say, I have the following single file component in Vue:
// Article.vue
<template>
<div>
<h1>{{title}}</h1>
<p>{{body}}</p>
</div>
</template>
After importing this component in another file, is it possible to get its template as a string?
import Article from './Article.vue'
const templateString = // Get the template-string of `Article` here.
Now templateString should contain:
<div>
<h1>{{title}}</h1>
<p>{{body}}</p>
</div>
It is not possible.
Under the hood, Vue compiles the templates into Virtual DOM render functions.
So your compiled component will have a render function, but no place to look at the string that was used to generate it.
Vue is not a string-based templating engine
However, if you used a string to specify your template, this.$options.template would contain the string.
set ref attribute for your component, and then you can get rendered HTML content of component by using this.$refs.ComponentRef.$el.outerHTML, and remember don't do this when created.
<template>
<div class="app">
<Article ref="article" />
</div>
</template>
<script>
import Article from './Article.vue'
export default {
name: 'App',
data() {
return {
templateString: ""
}
},
components: {
Article,
},
created() {
// wrong, $el is not exists then
// console.log(this.$refs.article.$el.outerHTML)
},
mounted() {
this.templateString = this.$refs.article.$el.outerHTML
},
}
</script>
Is it possible to update a property on all instances of a component?
If I have 10 instances of the component below on a page, I would like to set the currentTrack property to false on all of them. Is this possible? Can it be done from inside one of the components?
import Ember from 'ember';
export default Ember.Component.extend({
currentTrack: true,
});
I'm using Ember 2.12
You can use Ember.Evented for this use case.
Here, there is a simple twiddle for it.
template.hbs
{{your-component-name currentTrack=currentTrack}}
{{your-component-name currentTrack=currentTrack}}
{{your-component-name currentTrack=currentTrack}}
// btn for disabling
<a href="#" class="btn" onclick={{action 'makeAllcurrentTracksFalse'}}>To false</a>
controller.js
currentTrack: true,
actions: {
makeAllcurrentTracksFalse() {this.set('currentTrack', false)}
}
or in your-component-name.js - you can use the same action as above and it will be applied to all components
How about you create entries for what ever thing your're trying to achieve.
const SongEntry = Ember.Object.extend({
});
To create an entry you would call (probably add a song to playlist?)
songs: [],
addNewSongToList: function(songName) {
const newEntry = MyMusicEntry.create({
isCurrent: false,
title: songName
});
this.get('songs').pushObject(newEntry);
},
activateNewSong: function(newSongToActivate) {
this.get('songs').forEach(s => s.set('isCurrent', false);
newSongToActivate.set('isCurrent', true)
}
Template would look like this
{{each songs as |song|}}
{{my-song-component songEntry=song changeSong=(action "activateNewSong")}}
{{/each}}
//my-song-component.js
<div class="song-layout" {{action "changeSong" song}}>
This official guide describes how you can bind a boolean property to disabled attribute of a HTML element. Yet it talks about a controller.
I have a button, that when clicked transitions the route (sorry it has to be a button and cannot be a link-to):
/templates/trails.hbs
<button type="button" class="btn btn-primary" disabled={{isEditing}}
onclick={{route-action 'addNew'}}>Add New</button>
(route-action is a helper that allows me to use closure actions in routes)
/routes/trails.js
import Ember from 'ember';
export default Ember.Route.extend({
actions: {
addNew() {
this.transitionTo('trails.new');
}
}
});
So, after the button is clicked, the route is changed to 'trails.new'
/routes/trails/new.js
import Ember from 'ember';
export default Ember.Route.extend({
isEditing: true,
});
This property appears to be ignored and is not bound as I had expected it would be. I also tried adding a controller:
/controllers/trails/new.js
import Ember from 'ember';
export default Ember.Controller.extend({
isEditing: true,
});
So how does the official guide suggest something that seems to not work? What piece of ember magic am I missing here?
Your template is templates/trails.hbs but you set isEditing in a subroute controller controllers/trails/new.js
You need to have controllers/trails.js and deinfe isEditing in it.
So in routes/trails.js implement this :
actions: {
willTransition: function(transition) {
if(transtions.targetName === 'trails.new'){
this.controller.set('isEditing', true);
}
else{
this.controller.set('isEditing', false);
}
}
}
After some digging around I discovered that what I was trying to do is not the right way to go about this at all. I would have to add a controller/trails.js and put the property 'isEditing' in that.
So I refactored this into a component: add-new-button. This is a far more 'ember' way.
First, I need an initializer (thanks to this question):
app/initializers/router.js
export function initialize(application) {
application.inject('route', 'router', 'router:main');
application.inject('component', 'router', 'router:main');
}
export default {
name: 'router',
initialize
};
(this injects the router into the component, so I can watch it for changes and also 'grab' the currentRoute)
My code refactored into the component:
app/components/add-new-button.js
import Ember from 'ember';
export default Ember.Component.extend({
isEditing: function() {
let currentRoute = this.get('router.currentRouteName');
return ~currentRoute.indexOf('new');
}.property('router.currentRouteName')
});
templates/components/add-new-button.hbs
<button type="button" class="btn btn-primary" disabled={{isEditing}}
onclick={{route-action 'addNew'}}>Add New</button>
templates/trails.hbs
{{add-new-button}}
The beauty of this is now I can use this button on my other top level templates to trigger route changes to the new route for each resource (and disable the button on arrival at the new route).
NOTE
return ~currentRoute.indexOf('new');
is doing a substring check on the route, if it finds 'new' returns true, otherwise returns false. See this.
In ES6 it can be replaced with (so I have!):
return currentRoute.includes('new);
First of all : I'm using laravel spark and the given setup of vue that comes with spark.
I have a "home" component with the prop "custom". Within custom there's a "passwords" array. (Entry added by code of directive, it's initialized empty)
My component ( alist) which should be bound against the data
<template id="passwords-list-template">
<div class="password" v-for="password in list">
<ul>
<li>{{ password.name }}</li>
<li>{{ password.description }}</li>
</ul>
</div>
</template>
<script>
export default {
template: '#passwords-list-template',
props: ['list'],
};
</script>
Usage
<passwords-list :list="custom.passwords"></passwords-list>
Using vue devtools I can see that my data is updating, however my list is not. Also other bindings like
<div v-show="custom.passwords.length > 0">
Are not working ...
UPDATE : Parent component (Home)
Vue.component('home', {
props: ['user', 'custom'],
ready : function() {
}
});
Usage
<home :user="user" :custom="spark.custom" inline-template>
Update 2: I played around a little bit using jsfiddle. It seems like changing the bound data object using $root works fine for me when using a method of a component. However it does not work when trying to access it using a directive
https://jsfiddle.net/wa21yho2/1/
There were a lot of errors in your Vue code. First of all, your components where isolated, there wasn't an explicit parent-child relationship.Second, there were errors in the scope of components, you were trying to set data of the parent in the child, also, you were trying to set the value of a prop, and props are by default readonly, you should have written a setter function or change them to data. And finally, I can't understand why were you trying to use a directive if there were methods and events involve?
Anyway, I rewrote your jsfiddle, I hope that you find what you need there. The chain is Root > Home > PasswordList. And the data is in the root but modified in home, the last component only show it. the key here are twoWay properties, otherwise you wouldn't be able to modify data through properties.
Here is a snippet of code
Home
var Home = Vue.component('home', {
props: {
user: {
default: ''
},
custom: {
twoWay: true
}
},
components: {
passwordList: PasswordList
},
methods: {
reset: function () {
this.custom.passwords = [];
}
}
});
// template
<home :custom.sync="spark.custom" inline-template>
{{custom | json}}
<button #click="reset">
reset in home
</button>
<password-list :list="custom.passwords"></password-list>
<password-list :list="custom.passwords"></password-list>
</home>
Here is the full jsfiddle