I have a form and a live preview of what the form will create.
My model
//campaign.js
export default Model.extend({
title: attr('string'),
body: attr('string')
});
In the route
// new.js
export default Ember.Route.extend({
model () {
return this.store.createRecord('campaign', {
title: null,
body: null
})
}
});
My current implementation uses a component for the input
export default Ember.Component.extend({
keyPress(event) {
// binding code
}
});
And in the template
{{#title-input}}
{{/title-input}}
<div id="title-preview"></div>
My Feeling is that there is a cleaner or more idiomatic way to do this. I am new to ember so thank you for any help
While the use of Components are compelling they aren't required for capturing form input in ember. For what what its worth. For simple form input the route could be:
setupController() {
Ember.set('controller','newCampaign', {}); //set empty newCampaign
},
# Action hash would create the new record but only when you have some data.
actions: {
createCampaign(newCampaign) {
let newRecord = this.store.createRecord('campaign', newCampaign); //create record
newRecord.save().then(( /* response */ ) => {
this.transitionTo('campaigns'); //transition to different page.
}, (error) => { // Deal with an adapter error
//handle error
//rollback if necessary
});
}
}
The form or template could be:
{{input name="title" id="title" value=newCampaign.title type="text"}}
{{input name="body" id="body" value=newCampaign.body type="text"}}
Just a suggestion.
Jeff
Related
How can I set a value input that uses the v-model?
I Googled for this problem but not solved
I have an input like this:
<input type="text" name="customer_email" v-model="form.customer_email" id="email">
I need to set this input value to {{ auth()->user()->email }}
TRY THIS :)
data() {
return {
form: {
customer_email: "",
}
}
},methods:{
user(){
axios.get("api/profile").then(({data})=>{
(this.user = data)
this.form.customer_emeail = this.user.email
})
},
},created(){
this.user();
}
In your controller add this
public function profile()
{
return auth('api')->user();
}
then put this in your api.php
Route::get('profile','YourController#profile');
As you are using two way data binding v-model, you can simply set this value in the vue end.
let app = new Vue({
el:"#app",
data() {
return {
form: {
customer_email: "{{ auth()->user()->email }}",
......
......
}
}
},
......
......
});
I am learning Vuejs and I am stuck. Why can I see the messages get added to the object (in Chrome Vue debugger) yet it is not added to the div that contains the list?
My Vue Component:
<template>
<div id="round-status-message" class="round-status-message">
<div class="row">
<div class="col-xs-12" v-for="sysmessage in sysmessages" v-html="sysmessage.message"></div>
</div>
</div>
</template>
<script>
export default {
props: ['sysmessages'],
methods: {
scrollToTop () {
this.$el.scrollTop = 0
}
}
};
</script>
My Vue instance:
$(document).ready(function()
{
Vue.component('chat-system', require('../components/chat-system.vue'));
var chatSystem = new Vue({
el: '#system-chat',
data: function () {
return {
sysmessages: []
};
},
created() {
this.fetchMessages();
Echo.private(sys_channel)
.listen('SystemMessageSent', (e) => {
this.sysmessages.unshift({
sysmessage: e.message.message,
});
this.processMessage(e);
});
},
methods: {
fetchMessages() {
axios.get(sys_get_route)
.then(response => {
this.sysmessages = response.data;
});
},
processMessage(message) {
this.$nextTick(() => {
this.$refs.sysmessages.scrollToTop();
});
// updateGame();
}
}
});
});
My template call in HTML:
<div id="system-chat">
<chat-system ref="sysmessages" v-on:systemmessagesent="processMessage" :sysmessages="sysmessages" :player="{{ Auth::user() }}"></chat-system>
</div>
There are no compile or run time errors and I can see records added to the props in the vue chrome tool. I can also see empty HTML elements added to the div.
What have I missed?
UPDATE: My record structures:
response.data is an array of objects, each like this:
{"data":[
{"id":100,
"progress_id":5,
"message":"start message",
"action":"welcome"
},
{"id"....
e.message.message contains the text message entry, so just a string.
I am trying to access the message variable in each object during the fetchMessages method.
You're adding objects with sysmessage as the property.
this.sysmessages.unshift({
sysmessage: e.message.message,
});
But you are trying to view
v-for="sysmessage in sysmessages" v-html="sysmessage.message"
Based on your update, the code should be:
this.sysmessages.unshift({
message: e.message.message,
});
And you can leave the template as
v-html="sysmessage.message"
I have problems with creating routes with user's usernames. So idea is something like this: Click on path and go to that users profile. His link should be something like : http://www.something.com/usersUsername
I was reading and trying everything I found on internet about this but lot of stuff changed so I couldn't manage this.
Only thing I found usefull is that when page loads client ,,watch" paths first and then subscribes to a collection so I got ,,null" for path. Any help? My idea is to create something to waitOn for subscribe...
Packages: iron:router , accounts-ui , accounts-password
Here is code:
Start page, template:
<template name="početna">
<h1>Dobrodošli!</h1>
<h3>Registrujte se:</h3>
{{> register}}
<h3>Prijavite se:</h3>
{{> login}}
{{#if currentUser}}
<h2>Logovan si!</h2>
{{> logout}}
Profil
{{/if}}
Router JS file:
Router.configure({
layoutTemplate: 'okvir'
});
// * * * * * * //
Router.route('/', {
name: 'početna',
template: 'početna',
});
Router.route('/:username', {
waitOn: function(){
return Meteor.subscribe('userData'), Meteor.user().username
},
name: 'profil',
template: 'profil',
});
Simple HTML template file only to se if it works:
<template name="profil">
<h1>RADI</h1>
</template>
Thanks!
Here you go:
Router.route('/:username',{
name: 'profil',
waitOn: function () {
return Meteor.subscribe('userData', this.params.username)
},
action: function () {
this.render('profil', {
data: function () {
return Meteor.users.findOne({username: this.params.username});
}
});
}
});
EDIT:
With this.params.username will let anybody visit that profile, user or not. If you want to prevent that, you can use onBeforeAction()
onBeforeAction: function() {
if(Meteor.user() && this.params.username == Meteor.user().username){
this.next();
} else {
Router.go('/not-authorized') //or any other route
}
},
Luna, thanks for help! Luna's answer helped but I also needed:
1.) Helper to set value of username=username
Template["početna"].helpers({ username: function() { return Meteor.user().username } })
2.) Publish
Meteor.publish("userData", (username) => {
return Meteor.users.find({
username: username
})
});
I have the following code which calls an transitionToRoute('search') when a search-query is entered and the enter button is pressed or submit button is clicked.
However, my Router still won't show the searchQuery in the template where it says:
<p>You searched for: "{{searchQuery}}"</p>
and the URL looks like http://www.example.com/#/search/[object Object] when searching for something (which doesn't seem right to me).
(full code can be viewed over at: http://jsfiddle.net/Mn2yy/1/)
This is the relevant code:
Templates:
<script type="text/x-handlebars" data-template-name="container">
<button {{action "doSearch"}} rel="tooltip-bottom" title="search" class="icon"><i class="icofont-search"></i></button>
{{view Ember.TextField valueBinding="search" action="doSearch"}}
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="searchpage">
<h1>Search</h1>
{{#linkTo "home"}}Homepage{{/linkTo}}
<p>You searched for: "{{searchQuery}}"</p>
</script>
Application controller:
MyApp.ApplicationController = Ember.Controller.extend({
// the initial value of the `search` property
search: '',
doSearch: function() {
// the current value of the text field
var query = this.get('search');
this.transitionToRoute('search');
}
});
and the Searchpage route:
MyApp.SearchRoute = Ember.Route.extend({
setupController: function(controller) {
controller.set('searchQuery', this.get('query'));
},
renderTemplate: function() {
this.render('searchpage', { into: 'container' });
}
});
First, you need to define the dynamic segment in the router for the search route:
MyApp.Router.map(function() {
this.route("home", { path: "/" });
this.route("search", { path: "/search/:query" })
});
Then you set the searchQuery property on the application in the doSearch action. You also pass the query variable to the transitionToRoute method, since it'll fill in the dynamic segment.
MyApp.ApplicationController = Ember.Controller.extend({
// the initial value of the `search` property
search: '',
doSearch: function() {
// the current value of the text field
var query = this.get('search');
this.set('searchQuery', query);
this.transitionToRoute('search', query);
}
});
Since you need to access this property from the App.SearchController instance, you need to wire the 2 controllers together by using the needs API:
MyApp.SearchController = Ember.Controller.extend({
needs: ['application'],
application: Ember.computed.alias('controllers.application')
});
Aliased the controllers.application property to just application, to avoid too much typing eg. in the template.
Then you bind to this property in the search template:
<script type="text/x-handlebars" data-template-name="searchpage">
<h1>Search</h1>
{{#linkTo "home"}}Homepage{{/linkTo}}
<p>You searched for: "{{application.searchQuery}}"</p>
</script>
Last step: if you refresh the page at this point, searchQuery won't be automatically populated from the URL. Let's just fix that with the deserialize hook:
MyApp.SearchRoute = Ember.Route.extend({
deserialize: function(params) {
this.controllerFor('application').setProperties({
searchQuery: params.query,
search: params.query
});
},
renderTemplate: function() {
this.render('searchpage', { into: 'container' });
}
});
This will get the params from the URL and set up the application controller with the value of the query key.
That's pretty much it, hope I didn't miss anything!
Hey I'm having two different issues in my ember app, both of which involve bindings.
First, I have a binding firing when I don't want it to. Basically what I'm trying to achieve (I'm building a survey creator front-end app) is that when any text is entered into the 'name' field of a question, I want to add a new question object, which will render out another blank question at the end of the list of questions that the user is adding. This has the effect of there always being a new question, so an add question button is not required. The binding is working, and a new object is being added: however, since the binding is from the newest question object, the binding is triggered again when the new object is created, which in turn creates a new object, which triggers the binding again....which obviously eventually crashes the browser. I've tried using the Ember._suspendObserver function, but there isn't a lot of documentation on this, and I think I'm using it wrong - anyhow it isn't suspending the observer or pausing the binding. The observer in the code is around line 27 (contentsNameObserver)
The other issue I'm having -- I have a selection drop down box which selects what type of question the user wants (single answer, multi-choice, etc.) but the binding between the select box and the {{#each}} helper which renders the kind of question isn't triggering. I'm using the Ember.Select view helper, so there shouldn't be any issues with using get/set to fire the binding. I'm using a computed property to return an array of fields for the question type based on the value of the question type id. The computed property is in line 13 (App.SurveyContent.types), and the template templates/step3. Quick heads up that this app may be extended for more than surveys, hence 'questions' are often referred to in the code as 'content'.
I'm pretty new to ember (this is my first real app) so my code most likely has a lot of issues outside of these problems...so any comments on how I've structured my app would be hugely appreciated as well!
Javascript ember app:
App = Ember.Application.create({
rootElement: '#emberContainer'
});
App.SurveyContent = Ember.Object.extend({
name: "",
content_type: 1,
content_pos: 1,
hash: Em.A([]),
types: function() {
alert("redraw");
return App.ContentTypes[this.content_type-1].hash;
}.property()
});
App.Surveys = Ember.Object.create({
name: null,
start: $.datepicker.formatDate('mm/dd/yy' , new Date()),
end: $.datepicker.formatDate('mm/dd/yy' , new Date()),
themeID: 0,
contents: [App.SurveyContent.create()], //Pushing an instance of App.SurveyContent onto this
contentsNameObserver: function() {
context = this;
console.log("entering");
Em._suspendObserver(App.Surveys, "contents.lastObject.name", false, false, function() {
console.log("suspend handler");
context.contents.pushObject(App.SurveyContent.create());
})
}.observes("contents.lastObject.name")
});
App.ContentTypes = [
Ember.Object.create({name: 'Text question', id:1, hash: [Ember.Object.create({name: 'Question', help: 'Enter the question here', type: 'text'})]}),
Ember.Object.create({name: 'Multichoice question', id:2, hash: [Ember.Object.create({name: 'Question', help: 'Enter the question here', type: 'text'}),
Ember.Object.create({name: 'Answer', help: 'Enter possible answers here', type: 'text', multiple: true})]})
];
App.ViewTypeConvention = Ember.Mixin.create({
viewType: function() {
console.log(this);
return Em.get("Ember.TextField");
}.property().cacheable()
});
App.CRMData = Ember.Object.extend();
App.CRMData.reopenClass ({
crm_data: [],
org_data: [],
org_display_data: [],
loadData: function() {
context = this;
context.crm_data = [];
$.getJSON ("ajax/crm_data", function(data) {
data.forEach(function(crm) {
context.crm_data.pushObject(App.CRMData.create({id: crm.crm_id, name: crm.crm_name}));
crm.orgs.forEach(function(org) {
context.org_data.pushObject(App.CRMData.create({id: org.org_id, name: org.org_name, crm_id: crm.crm_id}));
}, context)
}, context)
context.updateOrganisations(5);
});
return this.crm_data;
},
updateOrganisations: function(crm_id) {
context = this;
this.org_display_data.clear();
console.log("clearing the buffer")
console.log(this.org_display_data)
context.org_data.forEach(function(org) {
if(org.crm_id == crm_id) {
context.org_display_data.pushObject(App.CRMData.create({id: org.id, name: org.name}));
}
}, context)
}
});
App.DateField = Ember.TextField.extend({
attributeBindings: ['id', 'class']
});
App.CRMSelect = Ember.Select.extend({
attributeBindings: ['id'],
change: function(evt) {
console.log(evt)
App.CRMData.updateOrganisations($('#crm').val())
}
});
App.ApplicationController = Ember.Controller.extend();
App.Step1Controller = Ember.ArrayController.extend({});
App.Step2Controller = Ember.ArrayController.extend({});
App.Step2Controller = Ember.ArrayController.extend({});
App.ApplicationView = Ember.View.extend({
templateName: 'app'
});
App.Step0View = Ember.View.extend ({
templateName: 'templates/step0'
});
App.Step1View = Ember.View.extend ({
templateName: 'templates/step1'
});
App.Step2View = Ember.View.extend ({
templateName: 'templates/step2',
didInsertElement: function() {
$( ".jquery-ui-datepicker" ).datepicker();
}
});
App.Step3View = Ember.View.extend ({
templateName: 'templates/step3',
});
App.Router = Em.Router.extend ({
enableLogging: true,
root: Em.Route.extend ({
showstep1: Ember.Route.transitionTo('step1'),
showstep2: Ember.Route.transitionTo('step2'),
showstep3: Ember.Route.transitionTo('step3'),
index: Ember.Route.extend({
route: '/',
connectOutlets: function(router){
router.get('applicationController').connectOutlet( 'step0');
}
}),
step1: Ember.Route.extend ({
route: 'step1',
connectOutlets: function(router){
router.get('applicationController').connectOutlet( 'step1', App.CRMData.loadData());
}
}),
step2: Ember.Route.extend ({
route: 'step2',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('step2')
},
}),
step3: Ember.Route.extend ({
route: 'step3',
connectOutlets: function(router) {
router.get('applicationController').connectOutlet('step3')
},
})
})
});
Ember.LOG_BINDINGS=true;
App.LOG_BINDINGS = true;
App.ContentTypes.forEach(function(object) {
object.hash.forEach(function(hash) {
hash.reopen(App.ViewTypeConvention);
}, this);
}, this);
Html templates (I've got these in haml, so this is just a representation of the important ones)
<script type="text/x-handlebars" data-template-name="app">
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="templates/step3">
<h1> Add content to {{App.Surveys.name}} </h1>
<br>
<div id = "accordion2" class = "accordion">
{{#each content in App.Surveys.contents}}
<div class="accordion-group">
<div class = "accordion-heading">
<a class = "accordion-toggle" data-parent = "#accordion2" data-toggle = "collapse" href = "#collapseOne">
{{content.name}}
</a>
</div>
<div id = "collapseOne" class = "accordion-body collapse in">
{{view Ember.TextField valueBinding="content.name" class="txtName"}}
<form class = "form-horizontal">
<div class = "accordion-inner">
<div class = "control-group">
<label class = "control-label" for ="organisation">
Content Type
<div class = "controls">
{{view Ember.Select contentBinding="App.ContentTypes" optionValuePath="content.id" optionLabelPath="content.name" valueBinding="content.content_type"}}
</div>
</div>
</div>
{{#each item in content.types }}
<div class = "control-group" >
<label class = "control-label" for = "organisation">
{{item.name}}
<div class = "controls">
{{view item.viewType }}
</div>
{{/each}}
</div>
</form>
</div>
{{/each}}
</div>
</div>
<br>
<div class = "btn" {:_action => 'showstep3'}> Next Step > </div>
</script>
I've solved the first issue, although I didn't get the suspendObserver property working I used an if statement to check the previous element, removing the infinite loop.
contentsNameObserver: function() {
context = this;
if(this.get('contents.lastObject').name) {
context.contents.pushObject(App.SurveyContent.create());
}
}.observes("contents.lastObject.name")
Any comments on how to get the _suspendObserver handler working would be appreciated though, it is something that should work but I'm doing something wrong
I've created a stripped down jsfiddle at http://jsfiddle.net/reubenposthuma/sHPv4/
It is set up to go straight to the problem step, step 3, so that I don't need to include all the previous templates.
I'm still stuck on the issue of the binding not firing though. The behaviour I'm expecting is that when the 'Content Type' dropdown box is changed, the text box underneath should change, it should re-render with two text boxes.
I realise this is an old question, but there is no documenation and precious little information I could find searching either, hence sharing what I found worked here.
What I found worked was to call Ember._suspendObserver as follows:
somePropertyDidChange: function(key) {
var that = this;
Ember._suspendObserver(this, key, null,
'somePropertyDidChange', function() {
// do stuff which would normally cause feedback loops
that.set('some.property', 'immune to feedback');
});
}.observes('some.property');
You can also use the multiple observer variant as follows:
somePropertiesDidChange: function(key) {
var that = this;
Ember._suspendObservers(this, ['some.property', 'another.property'],
null, 'somePropertiesDidChange', function() {
// do stuff which would normally cause feedback loops
that.set('some.property', 'immune to feedback');
that.set('another.property', 'also immune to feedback');
});
}.observes('some.property', 'another.property');
In my exact use case I actually called Ember._suspendObservers from an Ember.run.once() function which was setup by the observer since I wanted to make sure a number of dependant properties had settled before doing calculations which in turn would mutate some of those properties.