I'm working on an Ember project in which I have to specify the parameters of the component dynamically.
I have the following array in the .js controller:
componentParams: ["id", "name"]
What I want to do is to take the values in the array and use them in handlebars as the component parameter like this
{{component-name id=somevalue name="somevalue"}}
Could this be done?
An approach I use.
controller.js
navbarParams: {
titleToShow: 'General.ResearchProjects',
glyphicon: 'glyphicon glyphicon-globe',
infoText: 'information/project'
},
template.hbs
{{my-navbar params=navbarParams}}
my-navbar.hbs
<h1> {{params.titleToShow}} <span class={{params.glyphicon}}> </span> </h1>
If your parameters are queryParams
They should be defined like that
queryParams: ['foo', 'bar',],
foo: null,
bar: null
{{my-navbar first=foo second=bar}}
Honestly it depends, if you are stuck with that array - you can use computed properties to extract the proper array values. ( This is probably not recommended - a better approach would be to format your componentParams into an object ( like #kristjan's example).
If you are stuck with the array - and the positions will never change ( id will always be componentParams[0] & name will always be componentParams[1], you could try something like this ::
// controller
import Ember from 'ember';
const {
Controller,
computed,
get
} = Ember;
export default Controller.extend({
componentParams: ['id', 'name'],
componentName: computed('componentParams', {
get() {
return get(this, 'componentParams')[1];
}
}),
componentId: computed('componentParams', {
get() {
return get(this, 'componentParams')[0];
}
})
});
// template
{{my-component name=componentName id=componentId}}
// component/template
name:: {{name}}
<br>
id :: {{id}}
check out this twiddle for a working example
Does this help ??
Related
How can I use the return value of a computed property inside a data element that is rendered as HTML?
I have a data element that is HTML, and it looks like this:
contractContent: `<p>Hi ${this.brideName},</p>`
I've also tried this:
contractContent: `<p>Hi {{this.brideName}},</p>`
I am trying to pass in the name via this computed property:
brideName() {
return this.returnContracts[0].brideName.split(' ')[0]
},
But all I'm getting is undefined. If I just put brideName on the component as a test, it returns the first name of the bride just fine.
Where did I go astray?
Here is a fiddle with my dilemma
data() is invoked on component creation and is not reactive. contractContent should be a computed prop for your code to work:
export default {
data() {
return {
name: "Martina Navratilova",
}
},
computed: {
brideName() {
return `<p>Hi ${this.name.split(' ')[0]},</p>`
},
contractContent() {
return `<p>Hi there ${this.brideName}</p>`
},
},
}
updated fiddle
Instead of going with data -> computed -> data -> render,
go directly with computed -> render (html)
In your template you can render html like this
<span v-html="brideName" />
assuming your data structure of returnContracts to be like this
data: {
returnContracts: [
{
"brideName": "Emma Watson"
}
]
}
Then you can directly render brideName from computed
brideName(){
// assuming you'll have correct data, if data isn't valid this will cause crash.
return `<p>Hi ${this.returnContracts[0].brideName.split(' ')[0]},</p>`
}
here is a fiddle to help you out implementation
I want to create rootStore which contains others store. The problem is that the children contain properties like:
id: types.identifier(types.string),
And when I create the rootStore, I get an error from the child:
[mobx-state-tree] Error while converting {} to SomeModelStore: at path "/id" value undefined is not assignable to type: identifier(string) (Value is not a string), expected an instance of identifier(string) or a snapshot like identifier(string) instead.
I tried to use types.late but it did not help.
The solution I found is to wrap all properties into types.maybe
Examples:
error:
https://codesandbox.io/s/yvnznxyvyj?module=%2Fmodels%2FSomeModelStore.js
workaround:
https://codesandbox.io/s/0mv558yq50?module=%2Fmodels%2FSomeModelStore.js
Here https://codesandbox.io/s/yvnznxyvyj?module=%2Fmodels%2FSomeModelStore.js you create an empty object
.model("FirstStore", {
item: types.optional(SomeModelStore, {})
})
but type
SomeModelStore
didn't support empty fields. If you write like this
export const FirstStore = types
.model("FirstStore", {
item: types.optional(SomeModelStore, {
id: 'defaultId',
activate: false,
name: 'defaultName'
})
})
it will work. Or you can use "types.maybe" instead of "types.optional".
export const FirstStore = types
.model("FirstStore", {item: types.maybe(SomeModelStore)})
Also read about types.reference
I think it's a better way to use it in your case.
I have an array of blog post inside a script tag on the page that looks like this:
var posts = [
{article_name: "name", category: "test".....},
{article_name: "name2", category: "test2".....},
{...}
]
There are 30 objects in each array. I need to grab the categories in each object and assign it to the data prop in a vue instance. I have an empty array inside the vue instance like:
var blog_posts_nav = new Vue({
el: '#blog-posts-nav',
data: {
tags: []
}
})
I want to have each individual "category" in the 30 objects mapped to the "tags" array in the data prop. I tried doing it via the created() hook but it seems that the created hook can't access data? I tried:
created() {
posts.forEach( function (item) {
this.tags.push(item.category)
});
}
But I get an error in the console that says tags is undefined. Any help how I would deal with this? Basically I want to do work on a set of data and assign it to an array inside vue before outputting it to the page and in a manner that vue can interact with the data.
So first and foremost, is the created() hook (or any hook) the best way to go about this? Or should I be using something like methods or computed?
What is the "best practices" way and how would I go about achieving this?
It's not the created() method that can't access this, it's the inner context of forEach.
You can capture this to a local constant first, then access it inside the loop.
console.clear()
var posts = [
{article_name: "name", category: "test" },
{article_name: "name2", category: "test2" },
]
Vue.component("tags",{
template:`
<div>
{{tags}}
</div>
`,
data(){
return {
tags: [],
}
},
created() {
const vm = this;
console.log('created', vm.tags)
posts.forEach( function (item) {
vm.tags.push(item.category)
console.log('created, posts.forEach', vm.tags)
});
}
})
new Vue({
el: "#app"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.js"></script>
<div id="app">
<tags></tags>
</div>
I have an list of objects in firebase called meals.
In this component I want to show information on only one meal so I pass an id field as a prop and I want to get only that meal from firebase.
This is what I tried. It didn't work because this.id was undefined:
import db from '#/firebase'
export default {
name: 'meal',
props: {
id: {
type: String,
required: true
}
},
firebase: {
meals: db.ref('meals').child(this.id)
}
}
Did I do something wrong or does the firebase call happens before the props are initialized?
EDIT:
I managed to do it using the created hook but it looks pretty bad. Is there any other way?
created() {
this.$bindAsObject('meal', db.ref('meals').child(this.id))
}
This one burned me pretty hard. You have to use the firebase function AND create a reference to your variable like this:
import db from '#/firebase'
export default {
name: 'meal',
props: {
id: {
type: String,
required: true
}
},
firebase() {
const id = this.$props.id // Pass the reference instead of the prop directly
return {
meals: db.ref('meals').child(id)
}
}
}
According to the official doc of VueFire, your way to use the creation hook is exactly correct. Firebase basically works in a different lifecycle with Vue.js, so you need to use that doller syntax provided VueFire.
Coming to Ember from Rails, one of the places I'm struggling is trying to figure out Ember's definitions of models, views, and controllers.
I'm just testing out some sample Ember code. I'm getting my user events via the GitHub API, and I want to change the type name into something readable.
I have a jsbin here, but here's the gist:
App = Ember.Application.create();
App.IndexRoute = Ember.Route.extend({
model: function(){
return Ember.$.getJSON('https://api.github.com/users/thenickcox/events').then(function(data){
return data.splice(0,7);
});
}
});
I have a method that types a type and returns a string:
interpretType: function(type){
if (type === 'PushEvent') {
return 'Pushed';
}
return name;
}
In Rails, this would go on the model. But the only model here is the one that Ember created in memory by default (right?). So then I thought, it's something that each member of the array needs, because here's the view:
<h3> Some events</h3>
<ul>
{{#each}}
<li>I {{interpretType(type)}} to {{repo.name}}</li>
{{/each}}
</ul>
So is that something that goes on Ember.ArrayController? I tried that, like this:
App.IndexController = Ember.ArrayController.extend({
interpretType: function(type){
if (type === 'PushEvent') {
return 'Pushed';
}
return name;
}.property()
});
That just gave me an error. Where do I put this?
PS. So you don't have to look at the GitHub API, here's an example JSON object:
{
id: "1890853674",
type: "CreateEvent",
actor: {
id: 702327,
login: "thenickcox",
gravatar_id: "63f35d9e50dfd73281126b051a51668a",
url: "https://api.github.com/users/thenickcox",
avatar_url: "https://2.gravatar.com/avatar/63f35d9e50dfd73281126b051a51668a?d=https%3A%2F%2Fa248.e.akamai.net%2Fassets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png&r=x"
},
repo: {
id: 14463966,
name: "thenickcox/whiskey_taster",
url: "https://api.github.com/repos/thenickcox/whiskey_taster"
},
payload: {
ref: "master",
ref_type: "branch",
master_branch: "master",
description: "My first ember/rails app"
},
public: true,
created_at: "2013-11-17T09:00:17Z"
},
Here is an updated JSBin
Basically, the each can specify an itemController to decorate the model.
App.EventController = Ember.ObjectController.extend({
interpretType: function(){
var type = this.get('model.type');
if (type === 'PushEvent') {
type = 'Pushed';
}
return type;
}.property('model.type')
});
Handlebars doesn't have functions as you've written it, but since we are now using the event controller which wraps the single model, we just refer to interpretType to do the translation:
{{#each itemController='event'}}
<li>{{interpretType}} to {{repo.name}}</li>
{{/each}}
Put it inside an Ember.ObjectController
ArrayController's deal with methods related to the collection of data from the model, whereas ObjectController deals with methods related to the specific object.
I'm also learning Ember from a Rails background.
If you haven't already come across this, you will definetely want to check out ember-tools, it's a command line generator very similar to what we've got in rails. I cant imagine building an Ember app without something like it..