I have an issue attempting to create a "clone" of data on a page. The summary is that I have a create customer jobs screen and a separate manage customer jobs screen. These both work fine. I have a need to "clone" already existing customer job data to create a new similar set of jobs. So in my management screen, I have a clone button. I want this to head off to my "create" page with the data pre-populated with the original data, minus one or two pertinent identifying pieces of information, e.g. Job Name.
My issue is I cannot seem to get the post to redirect off to the Create page with my new data in the view model. I can't have a submit action as this saves the data; the data I'm cloning won't be persisted between the manage and create screens.
Here is where I'm at:
In my CSHTML:
<button type="button" name="btnClone" value="btnClone" id="btnClone" formaction="CloneJob" class="btn btn-primary" style="width: 150px;">Clone</button>
This fires off a click event to grab the current jobId which then fires off this function:
function CloneJob(jobId) {
$.post('CloneJob', { JobId: jobId }, function (data) {
window.document(data);
});
}
In my controller, I have this:
[HttpPost]
public ActionResult CloneJob(Guid jobId) {
// logic which wipes off the old data job from the view model
// eg names, ids, etc. but leaves the actual job details intact
return View("CreateJob", manageJobViewModel);
From this, I can see the HTML being returned in the IE/Chrome debug network section, but it won't go off to my CreateJob view populated with the data.
What am I missing?
First, if you're going to replace the entire tab/window contents, don't use AJAX. There's no point, and it's just something extra to maintain. Utilize a standard link, instead. Second, a POST should only be made when something is changing. This here should just be a simple GET request.
Create a link to the "create" URL and pass an id for the job that's being cloned. In the create action, then, you can look up that job from the database, based on that id, populate your view model with the data, and return it with the view.
If you want the appearance of a button, you can just style the link to look like a button, but you should use a standard old <a> tag, because that's the semantic meaning here.
I have managed to get further in that it now "works". My controller is unchanged, but my javascript is now:
$.when($.get('CloneJob', {jobId: jobId}))
.then(function (response)
{ $("body").html(response); });
I'm aware that this is still ajax, but in the face of an alternative that is more technically acceptable, this does work. Would be happy to take any advaces for a more technically neater approach as I understand that this is a bit of a misuse of ajax.
I have managed to get this cracked.
My cshtml now contains this code:
#Html.ActionLink("Clone Job", "CloneJob", null, new { #class = "btn btn-default", #style = "width: 150px" })
My controller just grabs the current viewmodel from the session and uses it to clone the job. Then, the controller now simply returns the view with the new model I set up within the method.
return View("CreateJob", clonedJobViewModel);
It's simple now when I look at it, but it's the getting there part.
Related
Hello I have a small website where data is passed between pages over URL.
My question is can someone break into it and make it pass the same data always?
For example let say, when you click button one, page below is loaded.
example.com?clicked=5
Then at that page I take value 5 and get some more data from user through a form. Then pass all the data to a third page. In this page data is entered to a database. While I observe collected data I saw some unusual combinations of records. How can I verify this?
yes. as javascript is open on the website, everyone can hack it.
you will need to write some code on you backend to validade it.
always think that you user/costumer will try to hack you sytem.
so take precautions like, check if user is the user of the session, if he is logged, if he can do what he is trying to do. check if the record that he is trying get exists.
if u are using a stand alone site, that u made the entire code from the ashes, you will need to implement this things by yourself.
like using the standard php session, making the data validation etc.
or you can find some classes that other people have made, you can find a lot o this on google. as it is a common problem of web programing.
if u are using a backed framework that isnt from another world, probably already has one. sp, go check its documentation.
html:
<a id = 'button-one' name = '5'> Button One </a>
javascript:
window.onload = function() {
document.getElementById('button-one').onclick = function() {
changeURL(this.attributes.name.value);
};
};
function changeURL(data) {
location.hash = data;
}
I have an auto refresh method (something like below) in my controller. In which I will update data in ViewBag, which I use in label of view. But I'm unable to refresh my label automatically. Please help me.
var waitHandle = new AutoResetEvent(false);
ThreadPool.RegisterWaitForSingleObject(waitHandle,(state, timeout) =>
{
// my viewbag
viewbag.time = DateTime.Now.TimeOfDay;
viewbag=date
}, null, TimeSpan.FromSeconds(5), false);
MVC doesn't work like WebForms (where reassigning a value populates to the UI). You'll need to write a separate action (or WebAPI method) and use AJAX/JavaScript to pull updates. (Alternatively you could use SignalR, but that may be overkill).
Also, if this is a recurring event, you may want to look into a library like Quartz.NET to perform the action. Then, post updates to a shared resource (looks like you're storing timestamp of last execution). From there, use setInterval/AJAX on the client to retrieve and display that value.
I am using Ember data and The RESTAdapter with an extension for Django.
Here is a JSBin
Here is how our routes are set up:
App.Router.map(function() {
this.resource('locations');
this.resource('location', {path:'locations/:location_id'}, function() {
this.resource('items', function() {
this.route('create');
});
this.resource('item', { path:'item/:item_id' }, function() {
this.route('edit');
});
});
});
App.LocationsRoute = Ember.Route.extend({
model: function () {
return this.get('store').find('location');
}
});
App.ItemsRoute = Ember.Route.extend({
model: function(){
//Get the model for the selected location and grab its item list.
//This will do a call to /locations/1/items
return this.modelFor('location').get('items');
}
});
Now this all works fine when we navigate to locations/1/items. The user is presented with a list of items relevant to the location with id 1.
When the user clicks one of these items it brings the url to #/locations/1/item/1 and displays the details of the item with id 1.
Now what doesnt work is this:
When I hit the back button the #/locations/1/items route loads but it does not have its data any more and no REST call to api/locations/1/items occurs. Even though the data displayed just fine when we first navigated to #/locations/1/items.
It is like Ember said "Well we already loaded that data, so we dont need to call the api again" but the data is somehow not being displayed in our template.
If I change the ItemsRoute model to this:
return this.get('store').find('item');
The scenario above works perfectly fine but the data is not based on our location.
Is there something I am missing with using this.modelFor? Or is my route set up incorrect?
Let me know if theres any extra info you need.
Update 1:
Having changed the model function to this I have discovered some new insights:
model: function(){
//Get the model for the selected location and grab its item list.
//This will do a call to /locations/1/items
var location = this.modelFor('location');
var items = location.get('items');
return items;
}
Upon first load of #/locations/1/items the location variable holds the location model and the items variable holds something which has a 'content' property, 'isFulfilled: true' and some other things.
This correctly works and displays the list of items. When i click on a particular item and got to #/locations/1/items/1 then hit the back button. The breakpoint triggers and location is still correctly populating with the location model.
However the items variable seems to just be a PromiseArray, do I need to somehow wait for the promise to be fulfilled before this model hook returns? I thought Ember already did this automatically? I suppose this means that the route is loading before the promise is fulfilled and thats why there is not data displayed?
Therefore my list is not populated correctly.
I'm on a phone, so I'll update this a bit later with more, but the problem is your location resource isn't a child of locations. Becaude of that, ember says why waste time fetching that route if it isn't a part of that resource path. It only hits the location route, which I'm betting you don't have a model hook for fetching the single location (at least based on the code above).
Ok, here is the deal. I have fixed the issue but have no idea why it fixed the issue.
Without further ado:
Here is a jsbin example of the exact setup I have. The only difference between the real one and the jsbin is the fact that I am using the RestAdapter instead of the FixtureAdapter. This is not technically true because I am using the ember-django-rest-adapter which extends the REST one.
The issue described in the question does not present itself in the jsbin but the exact setup with the ember-django-rest-adapter does present the issue.
The fix was to break the cyclic relationship between User -> Site -> Items -> User
For example if I comment out the 'locations' relationship in the User model, the back button works.
Or if I comment out the 'owner' relationship to User in the Item model the back button works.
I might ask a separate question to see what the reasoning behind the problem is, although if someone can shed any light in to why this is happening I'll happily accept the answer here.
I want to make a request to a server to get a bunch of news articles based off of what the user clicks on (recent, trending, etc). I'd like to be able to load the page first and show a loading bar while I wait for the response from the API. What I have, and although it works and returns the JSON data I need, will wait until the response comes back from the server before loading anything. This is all it is so far:
What I want to achieve is the following: Load up an empty array of objects, and then make API calls to articles incrementally (let's say grab all the articles from this hour, then last hour, then the hour before, and so on) and whenever I retrive articles populate the view (I'm assuming I can just inject them into a controller somehow) however I'm getting lost on the Emberisms on how to add to that array of objects. I think I need to make an ArrayController and then create a model in said array, then call a function to add to it, but as I said I'm lost as to how to add items into that controller on the fly
App = Ember.Application.create();
App.Router.map(function() {
this.resource('today');
});
App.TodayRoute = Ember.Route.extend({
model: function() {
return $.getJSON('/today');
}
});
To elaborate a bit on the reasoning for my question - I'm confused on how to approach this with ember. I'm familiar on how to do something like this using jquery, however I'm trying to learn the framework and am having a little bit of trouble originally knowing what the division of labor is between the two. I know the actual AJAX requests should be jquery, but I want to do as much as possible in Ember. If this however is something that should be done by jquery though, then that is fine as well!
The getJSON method looks to have a callback function on success you could use, something like this maybe?
App.TodayRoute = Ember.Route.extend({
model: function() {
$.getJSON('/today', function(data) {
// hide loading here
/ data is the JSON returned
});
}
});
Check out this link http://api.jquery.com/jQuery.getJSON/
I found the solution - and like everything with ember, it was very simple. I used some blank fixture data, then defined my controller as below.
App.ArticleRoute = Ember.Route.extend({
model: function() {
return App.Article.find();
}
})
Then upon pageload I call a method focused around
App.Article.createRecord({
// Attributes, pulling from the returned JSON
})
Simple! :)
Being fairly new to web dev and MVC4, I am encountering the same design issue repeatedly and was hoping someone could tell me what the right/supported/etc.. solution was in the MVC4 world:
Basically, I've drunk the view model koolaid and have view models for every view in my project, most of which are partial subviews dynamically updating on something of a single page application. All proceeds well generating/rendering the view, and then a user updates a number of values on the client side and it's time to update the server.
As an example, let's say it's a simple container view model:
public class Data {
public List<Prop> Props { get; set; }
}
public class Prop {
public string Id { get; set; }
public int Value { get; set; }
}
So let's just say the user is adding new props to the container. How do I get the modified object back to the server?
So far: If it's important that the server be in sync realtime, I can make a call with each addition/update on the server and then either keep things in sync on the client or simply have the server return the updated view. For simple scenarios like that, all is well.
But I find myself often in the case where I want the client to be able to manipulate the object (through the view/js/etc) and I don't really need to update on the server until they are done and submit. What I really want is to be able to pass the object down with the rendered view, interact with it via Javascript, and then pass the object back to the controller when all is done. How do I that? (Apologies it took a while to get to the point!)
Alternatives I've seen:
-- Quick & Dirty (Encode viewmodel properties to JavaScript in Razor): Which sure will put the object in javascript on the client, though it seems hack-ish to just be serializing the whole object into the client side html without any validation, etc. (Though I do realize eventually that's how the object is making it's way down, but it seems like you're bypassing the whole MVC4 object handling/parsing.)
-- Upshot.Js seemed promising with MS support at one time, but it seems that has died: Current status of Upshot.js
-- Breeze.js (http://www.breezejs.com/) seems to be an attempt to take over there, though my concern there is that it's fairly new and not much broad adoption yet.
Ultimately the thing that makes me feel like I'm missing what must be a somewhat obvious alternative is that all the pieces are clearly built into MVC4 already. When you use a form view for example, your fields are databound to the controls, and when the form is submitted, a parallel JSON object is created, sent to the controller, and then parsed into your POCO ViewModel object for you. That's basically the roundtrip I'm looking for (though retaining the full JSON object clientside). What's the "proper" way to handle that?
Knockout is actually exactly what I'm looking for (or another client side MV* framework), I simply didn't know how to use it.
Credit to #Mathletics and a couple of other SO solutions for point me in the right direction, especially: How to use knockout.js with ASP.NET MVC ViewModels?
The key is really to use the ko.mapping library, and Json encode or serialize the model on the server side. In particular, this script block is working well for me in strongly typed partial view:
<script type="text/javascript">
function viewModel() {
// Additional Knockout Here
};
var jsonModel = '#Html.Raw(JsonConvert.SerializeObject(this.Model))';
var mvcModel = ko.mapping.fromJSON(jsonModel);
var myViewModel = new viewModel();
var g = ko.mapping.fromJS(myViewModel, mvcModel);
ko.applyBindings(g);
</script>
And then you're all set up to use Knockout as per normal:
<div class="row">
<div class="span3">
<h3>Bound Value:</h3>
<span data-bind="text: PropertyName"></span>
</div>
</div>
(This requires knockout.js, knockout.mapping.js, and Newtonsoft.Json - though Json.Encode would work equally well.)
Passing the objects back is then easy. Update your object as normal, and when you pass it back to the controller, you already have you Json object ready to go and will be parsed as per normal to be passed back in as a POCO view model on the server side, completing the round trip.
From your description I can understand that you need to pass collection of objects to the controller and then update the collection. Model binding to collection will make things easier for you.
When binding to collection of single fields it becomes easier however when you want to bind to collection of objects then it becomes a little tricky. There are 2 options sequential binding and non- sequential binding.
Sequential binding:
Here is controller action where the form will be posted (HTTP POST action)
public ActionResult UpdateProps(ICollection<Prop> Props) {
return View(Props);
}
form method="post" action="/Home">
<input type="text" name="[0].Id" value="1001" />
<input type="text" name="[0].Value" value="Prop1" />
<input type="text" name="[1].Id" value="" />
<input type="text" name="[1].Value" value="NewProp" />
<input type="submit" />
</form>
When the form will be posted the values will go something like
[0].Id=1001&[0].Value=Prop1&[1].Id=&[1].Value=NewProp
You can see the indexing [0] and [1]. The model binder will rightly group the objects based on the indexing and you will get the expected collection. After the post your action will get 2 objects in ICollection Props. One with ID 1001 and another one which is newly added.
Now you will notice that if you add new prop dynamically then you need to index it properly using script. Or if that is hard for you then instead of indexing the elements you can post them in the above format. You need to parse the elements and post the form using script. The data should be arranged with index. The parsing gives you another advantage that you can post only the required elements i.e newly added or modified instead of posting them all thus saving the payload.
In non Sequential binding instead of series of number sequence the index is unique value like below:
[PropId1001].Id=1001&[PropId1001].Value=Prop1&[PropIdTemp233].Id=""&[PropIdTemp233].Value=NewProp