I'm working on an Emberjs application and I've got quite far with it
but i'm having an issue and I think that I've looked at every corner in the
web without finding the solution
Now my problem is that I have a dropdown that queries record on change "meaning" I have a "Dental" Department which contains "Braces" as a service
(service belongs to department) so when the user changes the dropdown option a query runs to filter the results again but within the results showing there is a text field which should be able to allow the user to search within the services shown accordingly to the selected department.
So far so good. The problem is that when the user starts typing to filter the data the condition in which services belongs to the selected department won't work any more so is there a way to use AND in my "Ember" controller to query records with more than one condition ?
Here is my Template
<div class="form-group">
{{#power-select
options=departments
selected=selectedDepartment
searchField="name"
placeholder="Select Department..."
onchange=(action (mut selectedDepartment))
dropdownClass="in-modal-dropdown"
renderInPlace=true
as |department|
}}
{{department.name}}
{{/power-select}}
</div>
{{#if selectedDepartment}}
<hr />
<div class="form-group has-feedback">
{{input value=searchText class="form-control input-sm" placeholder="Search Services" insert-newline="doSearch"}}
{{#if searchText}}
<i class="glyphicon glyphicon-remove form-control-feedback"></i>
{{/if}}
</div>
<br />
{{#each departmentServices as |service|}}
<button {{action 'selectService' service}} class="ux-product-override-for-request w-clearfix w-inline-block">
<div class="ux-product-icon"></div>
<div class="ux-product-title">{{service.name}}</div>
<div class="ux-product-price">{{service.price}} RS</div>
</button>
{{/each}}
{{/if}}
and my Controller
store: Ember.inject.service(),
departments: Ember.computed(function(){
return this.get('store').findAll('department')
}),
departmentServices: Ember.computed('selectedDepartment', 'search', function(){
if(this.get('search') == '' || this.get('search') == null){
console.log(this.get('search'));
return this.get('store').query('service', {
where: {
department: this.get('selectedDepartment.id')
}
})
} else {
return this.get('store').query('service', {
where: {
{ department: { this.get('selectedDepartment.id')} }
{ name: { contains: this.get('search')} }
}
})
}
}),
selectedDepartment: null,
{{input value=searchText - here you are using searchText but in departmentServices computed property you are using search but this is not the actual issue.
Issue is, this.get('store').query will return the Promise not the value so your implementation will not work out.(if you want to make it work you can try 3rd options Special promise-backed objects mentioned https://emberigniter.com/guide-promises-computed-properties/)
I will encourage you to introduce setDepartmentServices function which will query and update departmentServices property. and power-select for onchange instead of using the mut you can use onchange = (action 'setSelectedDepartment') and searchText input filed doSearch will call setDepartmentServices.
Here is the pseudo code,
setDepartmentServices() {
//this will do query and will set the result in departmentServices property.
}
actions: {
setSelectedDepartment(selectedDepartment) {
this.set('selectedDepartment', selectedDepartment);
//check if its ok to update departmentServices
this.send('setDepartmentServices');
}
doSearch() {
//check if its ok to update departmentServices
this.send('setDepartmentServices');
}
}
Related
Objective: I have a form interface that is being loaded with an object's current data for editing. The user opens this modal with the form that is loaded with the current info so they an either edit it or leave it
Currently working: The form loads with the data from my three objects (details, editSubEvents, instructions) and shows properly without issue
My problem: When I edit the fields and hit submit, I'm only currently dumping the submitted data object to make sure I have what I need. I get the eventID fine becasue it won't change and I get it from the original object. However, I need to store the new title, instruction, and subEvents (as an array) in order to submit them because they're obviously different from the origin ones
How can I properly store the new info from these input fields, including storing the new subEvent title and instructions as an array?
<div class="modal-body">
<div class="form-group row" v-for="detail in details">
<p class="modal-title">Title</p>
<input v-model="detail.title" type="text" class="form-control" id="EventTitle" name="EventTitle">
</div>
<div class="form-group row" v-for="subEvent in editSubEvents">
<p class="modal-title">SubEvent Title</p>
<input v-model="subEvent.title" type="text" class="form-control" id="newSubTitle" name="newSubTitle">
<p class="modal-title">SubEvent Instructions</p>
<textarea v-model="subEvent.instructions" type="text" class="form-control" id="newSubInstructions" name="newSubInstructions"></textarea>
</div>
</div>
data() {
return {
details: [],
instructions:[],
editSubEvents:[],
}
},
methods: {
updateEvent() {
let data = {
EventID: this.details[0].event_id,
title:
origin:
instructions:
subEvents: //needs to be an array
};
console.dir(data);
}
}
All of the properties of your data object can be bound to the UI elements (and most of them are, going by your template example code). The properties of the data object are accessible through the Vue component's this.
new Vue({
el: '#vueApp',
data() {
return {
details: [],
instructions:[],
editSubEvents:[],
}
},
methods: {
updateEvent() {
const data = {
EventID: this.details[0].event_id,
title: this.details[0].title,
origin: this.details[0].origin,
instructions: this.instructions,
subEvents: this.subEvents,
};
console.dir(data);
}
}
}
Yes me again!
This is my project
I want to pull the data from the json file when I fill in the required fields and press the button.
For example, let's write the developer part to the jobs section. then select istanbul as an option and click Find!.
var app = new Vue({
el: "#app",
data: {
founded: [],
search: ""
},
created() {
fetch("job.json")
.then(res => {
return res.json();
})
.then(res => {
this.founded = res.items;
});
},
computed:{
filteredFounded: function(){
return this.founded.filter((items)=> {
return items.positionName.match(this.search)
});
}
}
});
<div class="header">
<h4>Get Job</h4>
</div>
<div id="app" class="nested">
<div class="card w-50">
<div class="search">
<input type="text" class="job" v-model="search" placeholder="Job...">
<select name="" class="city" id="">
<option value="Seçiniz">Seçiniz</option>
<option value="İstanbul">İstanbul</option>
<option value="Ankara">Ankara</option>
<option value="İzmir">İzmir</option>
<option value="Çanakkale">Çanakkale</option>
</select>
</div>
<div class="find">
<button>Find!</button>
</div>
<div class="card-body" v-for="items in filteredFounded">
<h5 class="card-title">{{items.companyName}}</h5>
<p class="card-text">{{items.positionName | to-uppercase}}</p>
<p class="card-text">{{items.cityName}}</p>
<p class="card-text">{{items.townName}}</p>
<p class="card-text">{{items.distance}}</p>
Go!
</div>
</div>
</div>
<script src="script.js"></script>
If I understand your issue:
the view updates on each form change since you bound the card-body repeating div directly to the filtering process, so the "Find!" button isn't used
you don't consider the city selection
To fix these, bind a model to the city selector, and declare separate variables for the JSON data and for the selected data:
<select name="" class="city" id="" v-model="city">
and:
data: {
search: "",
sourceJobs: [],
selectedJobs: [],
city: ""
}
Then put you JSON data in sourceJobs on creation:
fetch("job.json").then(function (res) {
this.sourceJobs = res.json();
});
Side note: this architecture will not be viable for large JSON data, maybe you should consider filtering data through a call to your back-end API... but that's not the current question.
Now that your form data is bound to data.search and data.city, and that your pool of jobs is stored in data.sourceJobs, you want to have a method (no more computed) to filter data.sourceJobs and copy the resulting subset in data.selectedJobs:
methods: {
selectJobs: function () {
this.selectedJobs = this.sourceJobs
.filter((job) => {
return job.cityName === this.city && job.positionName.match(this.search);
})
}
}
Finally, call this method when the "Find!" button is clicked:
<button v-on:click="selectJobs">Find!</button>
In case you change you architecture to go for an API call to do the filtering, then you just need to remove that created part and do the API call from within the selectJobs method.
Side, unrelated note: find/found/found (successful result of searching) vs. found/founded/founded (create, build, set a base for something - a city, a company...).
<!-- Facets in the v-for below is an array of objects, each element (facet) in the
facets array has a property which is an array of facetItems. -->
<div class="row" v-for="(facet, facetsIndex) in facets" :key="facetsIndex">
<!-- Since we are inside the v-for, it creates a search input for each facet.
Each facet search input will only search for facetItems belonging to that facet.
We know which facet to search in because we pass the facetIndex to the searchFilter function. -->
<input type="text" #keyup="searchFilter(facetsIndex)">
<div v-if="facet.facetItems.length > 0">
<div class="facet-header">{{config[core.toLowerCase()].displayNames[facet.facetName]}}</div>
<div class="row facet-scroll" >
<!-- The v-for below is used to iterate over the facetItems for each facet. displayFacetItems() is called
for each array of facetItems corresponding to each facet on initial render. displayFacetItems() is also called
on each keyup event emitting from the corresponding facet search input. displayFacetItems() should return an
array of facetItems objects, and when a search input is entered, it should return a filtererd array of facetItems
based on the search results. -->
<div class="facet-item" v-for="(item, facetItemIndex) in displayFacetItems(facetsIndex)" :key="facetItemIndex">
<facet v-bind:item="item" v-bind:facet="facet"></facet>
</div>
</div>
<hr class="divider"/>
</div>
</div>
methods: {
searchFilter (facetsIndex) {
let searchTerm = event.currentTarget.value
this.displayFacetItems(facetsIndex, searchTerm)
},
displayFacetItems (facetsIndex, searchTerm) {
if (!searchTerm) return this.facets[facetsIndex].facetItems
return this.facets[facetsIndex].facetItems.filter((facetItem) => {
return _.includes(facetItem.name.toLowerCase(), searchTerm.toLowerCase())
})
}
},
Please see the comments in the code above for an explanation of what's happening in my code.
I don't understand why my code above isn't working. I'm trying to implement search functionality for each facet. When searching, the filtering should only happen for facetItems belonging to that specific facet.
I've been able to verify that displayFacetItems does return an array of filtered facetItems but for some reason the filtered array isn't updated in the DOM.
This might have something to do with Vue's data binding or the process in which Vue updates the DOM. Any pointers on what I'm doing wrong is greatly appreciated.
My code took inspiration from this article:
https://nickescobedo.com/1018/introduction-to-vue-js-filtering-with-lodash
You can see my jsfiddle for search items on Vuejs. I hope this will help you.
<div id="app">
<label>
Search name: <input type="text" v-model='searchString'>
</label> <br>
<transition-group tag='ul' name='my-list'>
<li v-for='item in filteredList' :key='item.id' class="my-list-item">
{{item.name}}
</li>
</transition-group>
</div>
<script>
const app = new Vue({
el: '#app',
beforeMount() {
const req = fetch('https://jsonplaceholder.typicode.com/users');
req.then(response => {
if (response.ok) {
return response.json();
}
throw new Error('Bad request: ' + response.status);
})
.then(users => {
this.users = users;
this.nextId = this.users.length + 1;
});
},
data: {
searchString: '',
users: [
{id: 1, name: 'Alex'},
{id: 2, name: 'Bob'},
{id: 3, name: 'Sandy'},
],
},
computed: {
filteredList: function() {
const searchString = this.searchString.toLowerCase();
return this.users.filter(item => item.name.toLowerCase().includes(searchString));
}
}
});
</script>
The param facetsIndex is not reactive, please take a look at your jsconsole, you should have a warning about this and not only this, your code logic is totally wrong.
You could use a v-model on input,
create a filteredFacets property,
use this property in your loop.
Just an example, you should read more about how VueJS works :
https://v2.vuejs.org/v2/guide/
I am new to Javascript and Meteor and having some trouble getting the Meteor autocomplete package from Mizzau to work correctly. I can get the form to autocomplete just fine, but having trouble getting it to filter my todos. The end result I am hoping for is to enter in a todo into the autocomplete and have it filter the subscription, I would also settle for search and going from there. I will also say my initial state is the list returns 0 todos (none are displayed) I feel like I may be close. A good part of my code came from this: Meteor - autocomplete with "distinct" feature?
T
Does it have something to do with my subscription?
Here is my server side publish call:
Meteor.publish("todosAuto", function(selector, options) {
Autocomplete.publishCursor(Todos.find(selector, options), this);
this.ready();
});
My client side subscription:
Meteor.subscribe('todosAuto');
The relevant part of my template:
<div class="container todoFormSec">
<div class="col-md-4">
{{> inputAutocomplete settings=settings id="msg" class="form-control" placeholder="Search..."}}
</div>
<div class="row">
<div class="col-md-5 pull-right">
<h1 class="todocountstyle text-right">You Have {{ todoCount }} Todos</h1>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="todos">
{{ #each todosAuto }}
{{ >todo }}
{{ /each }}
</div>
</div>
</div>
</div>
And my settings:
Template.home.helpers({
todos: function() {
return Todos.find();
},
todoCount: function() {
return Todos.find({userid: Meteor.userId()}).count();
},
settings: function() {
return {
position: "top",
limit: 5,
rules: [
{
token: '#',
collection: 'Todos',
field: "title",
subscription: 'todosAuto',
template: Template.titlePill
},
{
token: '#',
collection: 'Todos',
field: "categories",
options: '',
subscription: 'todosAuto',
matchAll: true,
template: Template.dataPiece
}
]
};
}
});
Template.home.events({ // listen for selection and subscribe
"autocompleteselect input": function(event, template, doc) {
Meteor.subscribe("todosAuto", doc.title);
}
});
In the past I tried to use this autocomplete package you are having trouble with. I found it just wasn't high fidelity enough for my needs. Therefore I recommend Twitter typeahead and bloodhound to you. I am very happy with the following packages ajduke:bootstrap-tokenfield in combination with sergeyt:typeahead
The time investment is well worth it. See my previous posting for examples. Here are more examples.
If the above is too complicated, try jeremy:selectize. Point is that there are plenty of better packages out there.
I have a template in wish I have 2 components that represents different Models. I need to create a model that contains both datas, for each component. If I have only one model everything works fine, but when I add other one, then this error happens in the console.
Error while processing route: events record is undefined ember$data$lib$system$store$finders$$_find/</<#http://localhost:1361/test/frontend/bower_components/ember-data/ember-data.prod.js:7475:11
Backburner.prototype.run#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:222:18
ember$data$lib$system$store$$Store<._adapterRun#http://localhost:1361/test/frontend/bower_components/ember-data/ember-data.prod.js:13133:16
ember$data$lib$system$store$finders$$_find/<#http://localhost:1361/test/frontend/bower_components/ember-data/ember-data.prod.js:7470:1
tryCatch#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:53070:14
invokeCallback#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:53085:15
publish#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:53053:9
#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:31253:7
Queue.prototype.invoke#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:901:9
Queue.prototype.flush#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:965:11
DeferredActionQueues.prototype.flush#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:765:11
Backburner.prototype.end#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:158:9
Backburner.prototype.run#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:226:13
run#http://localhost:1361/test/frontend/bower_components/ember/ember.prod.js:19151:12
ember$data$lib$adapters$rest$adapter$$RestAdapter<.ajax/</hash.success#http://localhost:1361/test/frontend/bower_components/ember-data/ember-data.prod.js:1728:15
n.Callbacks/j#http://localhost:1361/test/frontend/bower_components/jquery/dist/jquery.min.js:2:26920
n.Callbacks/k.fireWith#http://localhost:1361/test/frontend/bower_components/jquery/dist/jquery.min.js:2:27738
x#http://localhost:1361/test/frontend/bower_components/jquery/dist/jquery.min.js:4:11251
.send/b/<#http://localhost:1361/test/frontend/bower_components/jquery/dist/jquery.min.js:4:14765
The route of the template that contains the components is:
App.EventsRoute = Ember.Route.extend({
model: function()
{
return Ember.RSVP.hash({
event: this.store.find('event'),
featured: this.store.find('event', 'featured')
});
}
});
Here is my template:
<script type="text/x-handlebars" id="events">
<div class="col-xs-8 pull-left">
{{#each event as |event|}}
{{#event-box event=event}}{{/event-box}}
{{else}}
no events
{{/each}}
...
{{#each featured as |highlight|}}
{{#highlight-event hlevent=highlight}}
{{/highlight-event}}
{{else}}
No Highlights
{{/each}}
...
</script>
Does anyone knows why this error happens and what can I do to solve it?
this.store.find('event', 'featured') is invalid.
Also, find is deprecated.
With the latest Ember Data, you should use store.query().
You'd probably use it like
this.store.query('event', {featured: true})
or
this.store.query('event', {filter: 'featured'})
You'll need to adjust your adapter accordingly. Something like:
urlForQuery: function(query, modelName) {
if (query && query.featured === true) {
delete query.featured;
return this._buildURL(modelName, 'featured');
}
return this._super(query, modelName);
}
This should generate an URL like http://..../events/featured.