Django select2 with ModelChoiceField/ createTag (how to create a create new record) - django

How do I handle using select2 with ModelChoiceField, but enable adding a new record
I have the Select2 JS in place, and everything works well. I can use my queryset for the ModelChoiceField, and that works. However, if I try to add a new record all I get is the error "Select a valid choice. That choice is not one of the available choices". Which is true - in the view I tried to grab "ID 0" to then create a new record, but can't get past validation without the above error
Also, if I do get this to work, it will provide the "0" ID, but ultimately I require the "term" for the new record, but can't see how this is done
<script type="text/javascript">
$("#id_indoor_cooler").select2({
theme:"bootstrap4",
search_fields: ['model_number'],
tags: true,
createTag: function (params) {
var term = $.trim(params.term);
if (term === '') {
return null;
}
return {
id: 0,
text: term,
newTag: true, // add additional parameters,
value: "myCustomValue"
}
}
});
</script>

Related

Ember Data automatic refresh after save

The Ember page I'm working on is to display a of grid of operators and their jobs and have that list automatically update when the user creates a new job. My current best attempt will draw pre-existing jobs on page load, but the page doesn't refresh with any new jobs created using 'saveNewJob' even though I can see the new job in the Data view of Ember Inspector.
Here's the code with some '..snip..' inserted to focus on the important parts:
routes/scheduler.js
export default ember.Route.extend({
model: function() {
return {
jobs: this.store.query('job', {
location: session.location,
date: session.selectedDate
},
operators: this.store.query('operator', {
location: session.location
}
}
},
action: {
saveNewJob: function(params) {
var newJob = this.store.createRecord('job',{
//job properties from params
};
var thisRoute = this;
newJob.save().then(function(){ thisRoute.refresh() });
}
}
}
templates/scheduler.hbs
..snip..
{{#each model.operators as |op|}}
{{operator-row operator=op jobs=model.jobs}}
{{/each}}
{{outlet}}
templates/components/operator-row.hbs
<!-- Draw the grid for the operator -->
..snip..
<!--Draw jobs over grid -->
{{#if jobs.isFulfilled}}
{{#each jobsForOperator as |job|}}
{{operator-job job=job}}
{{/each}}
{{/if}}
component/operator-row.js
jobsForOperator: Ember.computed('jobs', function() {
var opId = this.operator.get('id');
var retVal this.jobs.filter(function(item) {
return item.get('operator').get('id') === opId;
});
<!-- Append some drawing properties to each job in retVal -->
..snip...
},
..snip..
I haven't seen a need to add anything to controller/scheduler. The operator-job component is a simple div that uses the drawing properties to correctly place/draw the div in the operator row.
There are various ways for a new job to be created, but I purposefully left them out because they all end up calling 'saveNewJob' and I am able to get a console.log statement to fire from there.
One solution I've tried is adding a 'this.store.findAll('job');' at the start of the model function and then using 'jobs: this.store.filter('job', function() { })' to create the model.jobs property. That sees neither the existing jobs nor the newly created job returned despite seeing my date and location matches return true.
So what am I doing wrong here? Is there a better way to get this refresh to happen automatically? Appreciate any help you all can give.
There is function for just this case DS.Store.filter. You use it instead of query. Something like this:
let filteredJobs = this.store.filter(
'job',
{location: session.location, date: session.selectedDate},
job => job.get('location') === session.location && job.get('date') === session.selectedDate
);

is handling custom server side errors in ember-data when saving model possible

Is there proper way to handle custom error when saving a model? To give an example, lets say I have a model with just two properties "name" and "value". And when I do :
var myModel = this.get('store').createRecord('myModel', {"name": "someName", "value": "someValue"});
myModel.save().then(function() {
//if success
//server responded with {"myModel:{"id":1,"name":"someName","value":"someValue"}"}
},function() {
//if failure
//server responded with {"error":"some custom error message"}
//BUT HOW TO CATCH THIS AND POSSIBLY REMOVE THE MODEL FROM THE STORE
});
One way to work around this is to make extra ajax call to check if the name is unique and then do the save. I am just wondering what is the best/elegant approach here.
Thanks,
Dee
EDIT : I thought it might help a bit to give more context on the server side of the things in groovy. So here it is:
In my controller I have :
def create() {
try {
newRow = someService.create(params)
render someService.list(newRow) as JSON//returns data in format needed by ember-data
}
catch (ValidationException ex) {
def errors = ["errors":[]]
ex.errors.allErrors.each{
if(it.arguments[0] == "fieldName" && it.code=="constrantViolated"){
errors.errors.push(["field":it.arguments[0],"message":"some custom message"])
}
}
//I am using 422 here because of post in http://stackoverflow.com/questions/7996569/can-we-create-custom-http-status-codes
render(status: 422, contentType: 'JSON', text: (errors as JSON))
}
}
Then in my ember controller:
var myModel = self.get('store').createRecord('myModel ', myModelDataInJSON);
myModel .save().then(function () {
//if success
},
function (response) {
myModel .deleteRecord();
var errors = $.parseJSON(response.responseText);
for (var key in errors.errors) {
//do something
}
});
deleteRecord will delete the record.
myModel.save().then(function(response) {
//if success
//server responded with {"myModel:{"id":1,"name":"someName","value":"someValue"}"}
},function(response) {
//if failure
//server responded with {"error":"some custom error message"}
//BUT HOW TO CATCH THIS AND POSSIBLY REMOVE THE MODEL FROM THE STORE
if(response.error=='no good'){
myModel.deleteRecord();
}
});
You can handle errors at model by adding properties into your model:
becameError: ->
# handle error case here
alert 'there was an error!'
becameInvalid: (errors) ->
# record was invalid
alert "Record was invalid because: #{errors}"
Check: How should errors be handled when using the Ember.js Data RESTAdapter?
Why wouldn't the answer be to just use the: DS.ERRORS CLASS?
From EmberJS docs:
For Example, if you had an User model that looked like this:
App.User = DS.Model.extend({
username: attr('string'),
email: attr('string')
});
And you attempted to save a record that did not validate on the backend.
var user = store.createRecord('user', {
username: 'tomster',
email: 'invalidEmail'
});
user.save();
Your backend data store might return a response that looks like this. This response will be used to populate the error object.
{
"errors": {
"username": ["This username is already taken!"],
"email": ["Doesn't look like a valid email."]
}
}
Errors can be displayed to the user by accessing their property name or using the messages property to get an array of all errors.
{{#each errors.messages}}
<div class="error">
{{message}}
</div>
{{/each}}
Is this question only focused on validation into the model? versus persistence/saving it, hence data is clean already before it hits a data store... Seems like you would still want error management at the adapter data store level, too.
That all said, why wouldn't you just use template or normal based JS validation at the UI control level?

How To Find Individual Record Based On Properties Other Than ID In Ember View

Is it possible to find an individual record based on its property in the views in Ember 1.0.0-rc.5? I've been searching around for days, but I still can't find anything that works.
For example, I would like to be able to do this:
App.Tag.find({name: 'some tag'}) which is supposed to return one record, but instead returns an array.
The name field is unique for all tags, so it should return only one object.
How can this be done?
Thanks
Problem solved! For people who might encounter the same problem, I will answer my question here. I ended up using the filter method to select one object. Details here http://emberjs.com/api/classes/Ember.Enumerable.html#method_filter
Code:
...
tagList = App.Tag.find().filter (item, index, enumerable) ->
return item.get('slug') is "slug title"
tag = tagList.get('firstObject')
...
When passing a query to a model's find method, you're invoking the findQuery method, which is designed to populate an array.
This is findQuery's definition:
findQuery: function(store, type, query, recordArray) {
var root = this.rootForType(type),
adapter = this;
return this.ajax(this.buildURL(root), "GET", {
data: query
}).then(function(json){
adapter.didFindQuery(store, type, json, recordArray);
}).then(null, rejectionHandler);
},
Which then calls didFindQuery upon success, to populate the array which is returned:
didFindQuery: function(store, type, payload, recordArray) {
var loader = DS.loaderFor(store);
loader.populateArray = function(data) {
recordArray.load(data);
};
get(this, 'serializer').extractMany(loader, payload, type);
},
So, assuming my understanding is correct, given that each 'name' in your case is unique, just get the first key of your array:
var tags = App.Tag.find({name: 'some tag'});
var tag = tags[0];

Ember-Data: How to use `DS.Adapter.findHasMany`

UPDATE
Note that this question applies to Ember Data pre-1.0 beta, the mechanism for loading relationships via URL has changed significantly post-1.0 beta!
I asked a much longer question a while back, but since the library has changed since then, I'll ask a much simpler version:
How do you use DS.Adapter.findHasMany? I am building an adapter and I want to be able to load the contents of a relationship on get of the relationship property, and this looks like the way to do it. However, looking at the Ember Data code, I don't see how this function can ever be called (I can explain in comments if needed).
There's not an easy way with my backend to include an array of ids in the property key in the JSON I send--the serializer I'm using doesn't allow me to hook in anywhere good to change that, and it would also be computationally expensive.
Once upon a time, the Ember Data front page showed an example of doing this "lazy loading"...Is this possible, or is this "Handle partially-loaded records" as listed on the Roadmap, and can't yet be done.?
I'm on API revision 11, master branch as of Jan 15.
Update
Okay, the following mostly works. First, I made the following findHasMany method in my adapter, based on the test case's implementation:
findHasMany: function(store, record, relationship, details) {
var type = relationship.type;
var root = this.rootForType(type);
var url = (typeof(details) == 'string' || details instanceof String) ? details : this.buildURL(root);
this.ajax(url, "GET", {
success: function(json) {
var serializer = this.get('serializer');
var pluralRoot = serializer.pluralize(root);
var hashes = json[pluralRoot]; //FIXME: Should call some serializer method to get this?
store.loadMany(type, hashes);
// add ids to record...
var ids = [];
var len = hashes.length;
for(var i = 0; i < len; i++){
ids.push(serializer.extractId(type, hashes[i]));
}
store.loadHasMany(record, relationship.key, ids);
}
});
}
Prerequisite for above is you have to have a well-working extractId method in your serializer, but the built-in one from RESTAdapter will probably do in most cases.
This works, but has one significant problem that I haven't yet really gotten around in any attempt at this lazy-loading approach: if the original record is reloaded from the server, everything goes to pot. The simplest use case that shows this is if you load a single record, then retrieve the hasMany, then later load all the parent records. For example:
var p = App.Post.find(1);
var comments = p.get('comments');
// ...later...
App.Post.find();
In the case of only the code above, what happens is that when Ember Data re-materializes the record it recognizes that there was already a value on the record (posts/1), tries to re-populate it, and follows a different code path which treats the URL string in the JSON hash as an array of single-character IDs. Specifically, it passes the value from the JSON to Ember.EnumerableUtils.map, which understandably enumerates the string's characters as array members.
Therefore, I tried to work around this by "patching" DS.Model.hasManyDidChange, where this occurs, like so:
// Need this function for transplanted hasManyDidChange function...
var map = Ember.EnumerableUtils.map;
DS.Model.reopen({
});
(^ Never mind, this was a really bad idea.)
Update 2
I found I had to do (at least) one more thing to solve the problem mentioned above, when a parent model is re-loaded from the server. The code path where the URL was getting split into single-characters was in DS.Model.reloadHasManys. So, I overrode this method with the following code:
DS.Model.reopen({
reloadHasManys: function() {
var relationships = get(this.constructor, 'relationshipsByName');
this.updateRecordArraysLater();
relationships.forEach(function(name, relationship) {
if (relationship.kind === 'hasMany') {
// BEGIN FIX FOR OPAQUE HASMANY DATA
var cachedValue = this.cacheFor(relationship.key);
var idsOrReferencesOrOpaque = this._data.hasMany[relationship.key] || [];
if(cachedValue && !Ember.isArray(idsOrReferencesOrOpaque)){
var adapter = this.store.adapterForType(relationship.type);
var reloadBehavior = relationship.options.reloadBehavior;
relationship.name = relationship.name || relationship.key; // workaround bug in DS.Model.clearHasMany()?
if (adapter && adapter.findHasMany) {
switch (reloadBehavior) {
case 'ignore':
//FIXME: Should probably replace this._data with references/ids, currently has a string!
break;
case 'force':
case 'reset':
default:
this.clearHasMany(relationship);
cachedValue.set('isLoaded', false);
if (reloadBehavior == 'force' || Ember.meta(this).watching[relationship.key]) {
// reload the data now...
adapter.findHasMany(this.store, this, relationship, idsOrReferencesOrOpaque);
} else {
// force getter code to rerun next time the property is accessed...
delete Ember.meta(this).cache[relationship.key];
}
break;
}
} else if (idsOrReferencesOrOpaque !== undefined) {
Ember.assert("You tried to load many records but you have no adapter (for " + type + ")", adapter);
Ember.assert("You tried to load many records but your adapter does not implement `findHasMany`", adapter.findHasMany);
}
} else {
this.hasManyDidChange(relationship.key);
}
//- this.hasManyDidChange(relationship.key);
// END FIX FOR OPAQUE HASMANY DATA
}
}, this);
}
});
With that addition, using URL-based hasManys is almost usable, with two main remaining problems:
First, inverse belongsTo relationships don't work correctly--you'll have to remove them all. This appears to be a problem with the way RecordArrays are done using ArrayProxies, but it's complicated. When the parent record gets reloaded, both relationships get processed for "removal", so while a loop is iterating over the array, the belongsTo disassociation code removes items from the array at the same time and then the loop freaks out because it tries to access an index that is no longer there. I haven't figured this one out yet, and it's tough.
Second, it's often inefficient--I end up reloading the hasMany from the server too often...but at least maybe I can work around this by sending a few cache headers on the server side.
Anyone trying to use the solutions in this question, I suggest you add the code above to your app, it may get you somewhere finally. But this really needs to get fixed in Ember Data for it to work right, I think.
I'm hoping this gets better supported eventually. On the one hand, the JSONAPI direction they're going explicitly says that this kind of thing is part of the spec. But on the other hand, Ember Data 0.13 (or rev 12?) changed the default serialized format so that if you want to do this, your URL has to be in a JSON property called *_ids... e.g. child_object_ids ... when it's not even IDs you're sending in this case! This seems to suggest that not using an array of IDs is not high on their list of use-cases. Any Ember Data devs reading this: PLEASE SUPPORT THIS FEATURE!
Welcome further thoughts on this!
Instead of an array of ids, the payload needs to contain "something else" than an array.
In the case of the RESTAdapter, the returned JSON is like that:
{blog: {id: 1, comments: [1, 2, 3]}
If you want to handle manually/differently the association, you can return a JSON like that instead:
{blog: {id: 1, comments: "/posts/1/comments"}
It's up to your adapter then to fetch the data from the specified URL.
See the associated test: https://github.com/emberjs/data/blob/master/packages/ember-data/tests/integration/has_many_test.js#L112
I was glad to find this post, helped me. Here is my version, based off the current ember-data and your code.
findHasMany: function(store, record, relationship, details) {
var adapter = this;
var serializer = this.get('serializer');
var type = relationship.type;
var root = this.rootForType(type);
var url = (typeof(details) == 'string' || details instanceof String) ? details : this.buildURL(root);
return this.ajax(url, "GET", {}).then(function(json) {
adapter.didFindMany(store, type, json);
var list = $.map(json[relationship.key], function(o){ return serializer.extractId(type, o);});
store.loadHasMany(record, relationship.key, list);
}).then(null, $.rejectionHandler);
},
for the reload issue, I did this, based on code I found in another spot, inside the serializer I overrode:
materializeHasMany: function(name, record, hash, relationship) {
var type = record.constructor,
key = this._keyForHasMany(type, relationship.key),
cache = record.cacheFor('data');
if(cache) {
var hasMany = cache.hasMany[relationship.key];
if (typeof(hasMany) == 'object' || hasMany instanceof Object) {
record.materializeHasMany(name, hasMany);
return;
}
}
var value = this.extractHasMany(type, hash, key);
record.materializeHasMany(name, value);
}
I'm still working on figuring out paging, since some of the collections I'm working with need it.
I got a small step closer to getting it working with revision 13 and based myself on sfossen's findHasMany implementation. For an Ember model 'Author' with a hasMany relationship 'blogPosts', my rest api looks like '/api/authors/:author_id/blog_posts'. When querying the rest api for an author with id 11 the blog_posts field reads '/authors/11/blog_posts'.
I now see the related blog posts being returned by the server, but Ember still throws an obscure error that it can not read 'id' from an undefined model object when rendering the page. So I'm not quite there yet, but at least the related data is correctly requested from the rest service.
My complete adapter:
App.Adapter = DS.RESTAdapter.extend({
url: 'http://localhost:3000',
namespace: 'api',
serializer: DS.RESTSerializer.extend({
keyForHasMany: function(type, name) {
return Ember.String.underscore(name);
},
extractHasMany: function(record, json, relationship) {
var relationShip = relationship + '_path';
return { url : json[relationShip] }
}
}),
findHasMany: function(store, record, relationship, details) {
var type = relationship.type;
var root = this.rootForType(type);
var url = this.url + '/' + this.namespace + details.url;
var serializer = this.get('serializer');
return this.ajax(url, "GET", {}).then(
function(json) {
var relationship_key = Ember.String.underscore(relationship.key);
store.loadMany(type, json[relationship_key]);
var list = $.map(json[relationship_key], function(o){
return serializer.extractId(type, o);}
);
store.loadHasMany(record, relationship.key, list);
}).then(null, $.rejectionHandler);
}
});
Here is my solution but it is on Ember-data 0.14, so the world has moved on, even if we are still on this code base:
findHasMany: function(store, record, relationship, details) {
if(relationship.key !== 'activities') {
return;
}
var type = relationship.type,
root = this.rootForType(type),
url = this.url + details.url,
self = this;
this.ajax(url, "GET", {
data: {page: 1}
}).then(function(json) {
var data = record.get('data'),
ids = [],
references = json[relationship.key];
ids = references.map(function(ref){
return ref.id;
});
data[relationship.key] = ids;
record.set('data', data);
self.didFindMany(store, type, json);
record.suspendRelationshipObservers(function() {
record.hasManyDidChange(relationship.key);
});
}).then(null, DS.rejectionHandler);
},
I found replacing the data with the ids worked for me.

Emberjs and Validation

How are people handling client side validation and ember?
Is there anything out of the box or a plugin that handles validation or are people just rolling their own?
https://github.com/dockyard/ember-validations might be useful. It also hooks up to Ember-easy-form
I would extend Ember.TextField (or whatever input type your validating) and use classBinding with a computed property. Here is the sample: http://jsfiddle.net/caligoanimus/7UNRd/
template:
<script type="text/x-handlebars" >
{{view App.AlphaNumField
placeholder="alpha-numeric data only"
valueBinding="App.alphaNumInput"}}
</script>
application:
App = Ember.Application.create({
AlphaNumField: Ember.TextField.extend({
isValid: function() {
return /^[a-z0-9]+$/i.test(this.get('value'));
}.property('value'),
classNameBindings: 'isValid:valid:invalid'
})
});
Another fully supported option and very logical if you are using bootstrap is to use bootstrap-validation plugin. In ember (ember-cli) this should be installed using bower:
bower install --save bootstrap-validation
then in ember-cli-build you must import dependencies:
//bootstrap-validator
app.import('bower_components/bootstrap-validator/dist/validator.js');
app.import('bower_components/bootstrap-validator/dist/validator.min.js');
This solution allows you to validate at html level, letting bootstrap do the 'dirty' job. For standard validations this will do the job simple and effortless.
I have been handling it in a very similar way to #caligoanimus, but validating on the focus out of the text box and appending an error message.
code:
App.TextFieldEmpty = Ember.TextField.extend({
focusOut: function() {
var valid = this.get('value') ? valid = true : valid = false;
this.$().next(".err").remove();
if(!valid){
this.$().addClass("invalid").after("<span class='err'>This field is required</span>");
} else {
this.$().removeClass("invalid")
}
}
});
Template:
<script type="text/x-handlebars">
{{view App.TextFieldEmpty}}
</script>
JSBIN:
http://jsbin.com/uriroYO/6/edit?html,js,output
I've had a lot of success with jQuery Validate:
App.MyView = Ember.View.extend({
templateName: 'my-form-template',
didInsertElement: function(){
$("#myForm").validate({
rules:{
fname:{
required: true,
maxlength: 50,
},
lname:{
required: true,
maxlength: 50,
},
email: {
required: true,
email: true,
maxlength: 200,
},
},
messages: {
email: {
email: "Enter a valid email address.",
},
},
});
}
});
Just using the Ember input helper, it's made my form validation very clean. You can take your jQuery Validate script and place it in a .js file as a function and just call that on didInsertElement.
jQuery Validate adds error messages below your fields with the message relating to the rule, and also allows you to trigger validation from any of your actions or events through the .valid() method.
We have created our own text fields which raise validation errors on focus out, and other events and other text fields extend them:
App.PhoneNumberField = App.TextField.extend({
validate: function(value) {
var self = this.$('input');
var id = self.attr("id");
var e164PhoneNumber = formatE164("AU",value);
var valid = true;
if(self.val().trim().length == 0 ){
valid = true;
}else{
valid = isValidNumber(e164PhoneNumber);
}
if (!valid) {
self.trigger(Storm.invalidInputEvent(id));
this.setErrorMessage("error","Phone Number does not look right");
}else {
self.trigger(Storm.validInputEvent(id));
this.clearMessages();
}
},
keyUp: function(evt) {
if(evt.keyCode >= 37 && evt.keyCode <=40)
{
return;
}
var textValue = this.$("input").val();
var input = this.$().find('input');
var formattedNumber = this.formatInput(textValue);
input.val(formattedNumber);
this.set('data',formattedNumber);
},
value: function() {
var phoneNumber = this.get('data');
if (phoneNumber) {
return phoneNumber;
} else {
return "";
}
}.property('data'),
data: null,
placeholder: function() {
return "";
}.property('placeholder'),
formatInput: function(textValue){
var formattedPhoneNumber = formatLocal("AU",textValue);
return formattedPhoneNumber;
}
});
I would use a model / persistance layer which uses a conventional "errors" object to save validation errors on the model.
Since Ember shines when it comes to observing changes, I would observe the changing errors object to determine whether or not should there be shown a validation message.
In my current setup I'm using Tower.js as framework, because it uses Ember as the View layer, and has a strong model / persistance layer. This layer allows me to define validations on model level. Each time I try to persist a model, it is validated and an error is thrown. In my views, this error aborts the "ideal" path and does not keep executing the workflow, instead it renders the validation errors in the template.