Python 在Knockout attr绑定下使用Jinja2模板

Python 在Knockout attr绑定下使用Jinja2模板,python,knockout.js,flask,jinja2,Python,Knockout.js,Flask,Jinja2,我正在使用Flask和Knockoutjs制作一个项目。我使用敲除来显示评论,下面还会显示评论者的姓名,单击会将此人带到该用户配置文件 这是密码 <a data-bind="attr: { href: '{{ url_for('user_profile')}}' + '/' + name() + '/' + uid() + '/' }"><p data-bind="text: name"></p></a> 因此,我更改了上面提到的代码片段kno

我正在使用Flask和Knockoutjs制作一个项目。我使用敲除来显示评论,下面还会显示评论者的姓名,单击会将此人带到该用户配置文件

这是密码

<a data-bind="attr: { href: '{{ url_for('user_profile')}}' + '/' + name() + '/' + uid() + '/'  }"><p data-bind="text: name"></p></a>
因此,我更改了上面提到的代码片段knockout attr文档

这意味着user_profile view没有获得所需的变量。在knockout中,实现这一点的方法是attr绑定。我已经提到了这个问题


但是没有任何东西能像预期的那样工作

我没有太多的敲除经验,但听起来不太正确,将用户配置文件放在引号内,尝试替换url\u函数参数中的引号字符:

<a data-bind="attr: { href: '{{ url_for(\'user_profile\')}}', name: name , uid: uid  }"><p data-bind="text: name"></p></a>  

我希望这能有所帮助


致以最诚挚的问候

您没有显示您的
用户配置文件
端点的定义,但我打赌它或多或少是这样的:

@app.route('/user/<name>/<uid>')
def user_profile(name, uid):
    # ...
然后,您的淘汰控制器将有一个
showProfile()
方法来发出重定向:

self.showProfile = function(user) {
    window.location = user.profileUrl
}
为此,您需要向Flask返回到Knockout应用程序的JSON字典添加一个
profileUrl
键。如果不显示应用程序的这一部分,我想这应该很容易添加,因为现在url完全在服务器中生成,您可以自由使用
url\u for()
并提供所有参数

解决方案#2 如果您希望修复上面显示的代码,那么您必须放弃使用
url\u for()
并完全在客户端构建url。我认为下面的例子应该很好:

<a data-bind="attr: { href: '/user/' + name() + '/' + uid() + '/'  }"><p data-bind="text: name"></p></a>
现在您可以为('user_profile')说
url\u
,然后返回
/user
,您可以在其中附加Javascript中的其余参数。有了烧瓶侧的这一变化,我相信您的第一个示例应该会起作用

作为旁注,我希望您知道,当点击链接时,会吹走您的淘汰应用程序,它将被替换为一个新页面(当然,可能有另一个淘汰应用程序实例)。另一种选择是使用一个单页应用程序,因此对用户配置文件的更改完全发生在Javascript端。当然,这会将更多的应用程序移动到客户端


我希望这有帮助

我认为问题在于数据和模板信息的分离

如果使用knockout,则通常在服务器上生成数据,并将其作为json数据发送到客户端的模板呈现

函数的
url\u是一个服务器端函数,因此不能在客户端模板中的客户端上运行它

正如@Miguel的回答一样,最简单/最好的方法是在服务器上生成url,作为数据的一部分,而不是模板的一部分

因此,用于生成数据的app.route可能是:

@app.route(“/posts/”)
def posts_list():
职位=[]
对于get_posts()中的post:#但是实际上您是从数据库中获取帖子的
author=get_author(post.author)#但实际上您可以获得作者信息
posts.append(
{“id”:post.id,
“title”:post.title,
“作者姓名”:作者[“显示姓名”],
“author_uri”:url_代表('user_profile',author[“name”],author[“id”]),
“内容”:post.content})
返回jsonify({“posts”:posts})
或者别的什么。您正在服务器端生成所有POST数据,因此您可以访问url_函数。然后,客户端只需呈现纯数据:

<div data-bind="foreach: posts">
  <div class="post">
    <div data-bind="text: content" class="content"></div>
    <div class="author_info">
      <a data-bind="attr: { href: author_uri }, text: author_name"></a>
    </div>
  </div>
</div> <!-- posts -->

然后在HTML app.route中,完成所有纯敲除模板后,初始化敲除视图,然后从posts json路由请求数据:

<!-- at the end of your HTML page/view, after including your models, and libs -->
<script type="text/javascript">
    // autogenerated:
    POSTS_URL="{{ url_for('posts_list') }}";

    // initialise views:
    postsview = new PostsView( data );
    ko.applyBindings(posts);

    // get initial data:
    $.getJSON(POSTS_URL, function (data) {
        // we've just got data sent to us from the posts_list route!
        postsview.posts(data.posts);
    });

</script>

//自动生成:
POSTS_URL=“{URL_for('POSTS_list')}}”;
//初始化视图:
postsview=新的postsview(数据);
ko.应用绑定(职位);
//获取初始数据:
$.getJSON(POSTS\u URL,函数(数据){
//我们刚收到从posts\U list路线发送给我们的数据!
postsview.posts(data.posts);
});
如果您尝试将jinja模板代码(服务器端)与knockout混合使用 模板化代码(客户端),然后您将遇到问题。你需要把他们当作一个整体来对待 完全独立的系统,并来回传递纯数据(通常是JSON) 第四

或者,您可以将数据直接嵌入到页面中,而不是发出单独的异步请求。但是,再次提醒您,要让您的淘汰代码完全不受jinja模板的影响,然后在页面末尾,您只需执行以下操作,而不是执行$.getJSON操作:

<script type="text/javascript">
    POSTS_DATA={{ posts |tojson|safe }};
    posts = new PostsView( POSTS_DATA );
    ko.applyBindings(posts);
</script>

POSTS|u DATA={{POSTS|tojson | safe}};
posts=新PostsView(posts\U数据);
ko.应用绑定(职位);
如果您以这种方式分离数据,那么就更容易进行推理,而且如果以后您决定切换到其他javascript引擎,您也可以这样做,因为数据包含您需要的所有内容。或者,如果您决定重新编写python引擎,或者如果您想制作一个本机应用程序,或者其他什么


我希望这有帮助

我的网站上有另一个解决方案

app.py:

@app.context_processor
def pass_context():
    return dict(
        rules=[[rule.rule, rule.endpoint.replace('_', '-').replace('.', '-')] for rule in app.url_map.iter_rules()]
    )
模板/base.jinja:

<script>
var routes = {};
{% for rule in rules -%}
    routes['{{ rule[1] }}'] = '{{ rule[0] }}';
{% endfor %}
</script>

var路由={};
{%用于规则中的规则-%}
路由['{rule[1]}}']='{{rule[0]}}';
{%endfor%}
在我的script.js中:

var rurlarg = /<(?:\w+:)?([\w_-]+)>/;
var urlFor = function(page) {
    var url,
        urlArgs,
        _ = [];
    if (! (page in routes)) {
        throw {
            name: 'IndexError',
            message: 'no such page: ' + page
        };
    }
    url = routes[page];

    if (arguments.length > 1) {
        urlArgs = Array.prototype.slice.call(arguments, 1);
        urlArgs.forEach(function(val, i) {
            url = url.replace(rurlarg, val);
        });
    }

    return url;
}
var rurlarg=/;
var urlFor=函数(第页){
var url,
urlArgs,
_ = [];
如果(!(路线中的页面)){
扔{
名称:'索引器',
消息:“没有这样的页面:”+页面
};
}
url=路由[页面];
如果(arguments.length>1){
urlArgs=Array.prototype.slice.call(参数,1);
forEach(函数(val,i){
url=url.replace(rurlarg,val);
});
}
返回url;
}
这不是理想的函数,但你明白了。例如,可以添加命名参数。或客户端的筛选规则(隐藏管理员端点等)

此lo
<!-- at the end of your HTML page/view, after including your models, and libs -->
<script type="text/javascript">
    // autogenerated:
    POSTS_URL="{{ url_for('posts_list') }}";

    // initialise views:
    postsview = new PostsView( data );
    ko.applyBindings(posts);

    // get initial data:
    $.getJSON(POSTS_URL, function (data) {
        // we've just got data sent to us from the posts_list route!
        postsview.posts(data.posts);
    });

</script>
<script type="text/javascript">
    POSTS_DATA={{ posts |tojson|safe }};
    posts = new PostsView( POSTS_DATA );
    ko.applyBindings(posts);
</script>
@app.context_processor
def pass_context():
    return dict(
        rules=[[rule.rule, rule.endpoint.replace('_', '-').replace('.', '-')] for rule in app.url_map.iter_rules()]
    )
<script>
var routes = {};
{% for rule in rules -%}
    routes['{{ rule[1] }}'] = '{{ rule[0] }}';
{% endfor %}
</script>
var rurlarg = /<(?:\w+:)?([\w_-]+)>/;
var urlFor = function(page) {
    var url,
        urlArgs,
        _ = [];
    if (! (page in routes)) {
        throw {
            name: 'IndexError',
            message: 'no such page: ' + page
        };
    }
    url = routes[page];

    if (arguments.length > 1) {
        urlArgs = Array.prototype.slice.call(arguments, 1);
        urlArgs.forEach(function(val, i) {
            url = url.replace(rurlarg, val);
        });
    }

    return url;
}